~samwhited/oslp

f06e52c6f565c343f3ebb56170fb113ace9fb1cb — Sam Whited 3 years ago 94b5c4a
Retab
29 files changed, 1163 insertions(+), 1279 deletions(-)

M art/ic_launcher.svg
M art/marker.svg
M build.gradle
D src/androidTest/java/com/samwhited/opensharelocationplugin/ApplicationTest.java
M src/main/java/com/samwhited/opensharelocationplugin/activities/AboutActivity.java
M src/main/java/com/samwhited/opensharelocationplugin/activities/LocationActivity.java
M src/main/java/com/samwhited/opensharelocationplugin/activities/SettingsActivity.java
M src/main/java/com/samwhited/opensharelocationplugin/activities/ShareLocationActivity.java
M src/main/java/com/samwhited/opensharelocationplugin/fragments/SettingsFragment.java
M src/main/java/com/samwhited/opensharelocationplugin/overlays/Marker.java
M src/main/java/com/samwhited/opensharelocationplugin/overlays/MyLocation.java
M src/main/java/com/samwhited/opensharelocationplugin/preferences/AboutPreference.java
M src/main/java/com/samwhited/opensharelocationplugin/util/Config.java
M src/main/java/com/samwhited/opensharelocationplugin/util/LocationHelper.java
M src/main/java/com/samwhited/opensharelocationplugin/util/SettingsHelper.java
M src/main/java/com/samwhited/opensharelocationplugin/util/UriHelper.java
M src/main/res/drawable/snackbar.xml
M src/main/res/layout-v21/activity_share_location.xml
M src/main/res/layout-v21/activity_show_location.xml
M src/main/res/layout/activity_about.xml
M src/main/res/layout/activity_share_location.xml
M src/main/res/layout/activity_show_location.xml
M src/main/res/menu-v21/menu_share_location.xml
M src/main/res/menu-v21/menu_show_location.xml
M src/main/res/menu/menu_share_location.xml
M src/main/res/menu/menu_show_location.xml
M src/main/res/values-de/strings.xml
M src/main/res/values-fr/strings.xml
M src/main/res/xml/settings.xml
M art/ic_launcher.svg => art/ic_launcher.svg +66 -132
@@ 1,134 1,68 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   width="48"
   height="48"
   viewBox="0 0 48 48"
   id="svg2"
   version="1.1"
   inkscape:version="0.91 r13725"
   sodipodi:docname="launcher.svg"
   inkscape:export-filename="/home/sam/Projects/OpenShareLocationPlugin/art/launcher.png"
   inkscape:export-xdpi="960"
   inkscape:export-ydpi="960">
  <metadata
     id="metadata12">
    <rdf:RDF>
      <cc:Work
         rdf:about="">
        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
        <dc:title />
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <defs
     id="defs10">
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient3913"
       id="radialGradient3883"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(-0.17139995,-0.11076121,-0.06027526,0.1990256,151.7408,-109.89045)"
       cx="262.33273"
       cy="945.23846"
       fx="262.33273"
       fy="945.23846"
       r="185.49754" />
    <linearGradient
       inkscape:collect="always"
       id="linearGradient3913">
      <stop
         style="stop-color:#ffffff;stop-opacity:1;"
         offset="0"
         id="stop3915" />
      <stop
         style="stop-color:#ffffff;stop-opacity:0;"
         offset="1"
         id="stop3917" />
    </linearGradient>
    <clipPath
       clipPathUnits="userSpaceOnUse"
       id="clipPath4277">
      <path
         inkscape:connector-curvature="0"
         d="M 23.999717,3.9995324 8.999718,40.589532 l 1.41,1.41 13.589999,-6 13.59,6 1.41,-1.41 z"
         id="path4279"
         style="fill:#00a000;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:8, 8;stroke-dashoffset:1.19999993;stroke-opacity:0.50196078" />
    </clipPath>
    <clipPath
       clipPathUnits="userSpaceOnUse"
       id="clipPath4323">
      <path
         style="fill:#00a000;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:8, 8;stroke-dashoffset:1.19999993;stroke-opacity:0.50196078"
         id="path4325"
         d="M 24.008442,4.0723566 9.0084404,40.662356 l 1.4100006,1.41 13.590001,-6 13.589999,6 1.41,-1.41 z"
         inkscape:connector-curvature="0" />
    </clipPath>
  </defs>
  <sodipodi:namedview
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1"
     objecttolerance="10"
     gridtolerance="10"
     guidetolerance="10"
     inkscape:pageopacity="0"
     inkscape:pageshadow="2"
     inkscape:window-width="1920"
     inkscape:window-height="1017"
     id="namedview8"
     showgrid="false"
     inkscape:zoom="9.8333331"
     inkscape:cx="6.8049159"
     inkscape:cy="29.859938"
     inkscape:window-x="0"
     inkscape:window-y="41"
     inkscape:window-maximized="1"
     inkscape:current-layer="svg2" />
  <path
     d="M0 0h48v48h-48z"
     fill="none"
     id="path4" />
  <path
     d="M24 4l-15 36.59 1.41 1.41 13.59-6 13.59 6 1.41-1.41z"
     id="path6"
     style="fill:#00a000;fill-opacity:1;stroke:#ffffff;stroke-opacity:0.50196081;stroke-linejoin:miter;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:8,8;stroke-dashoffset:1.19999993;stroke-linecap:butt"
     clip-path="url(#clipPath4323)" />
  <path
     style="display:inline;opacity:0.19211821;fill:url(#radialGradient3883);fill-opacity:1;stroke:none"
     d="m 28.626356,3.8952079 c 15.401473,0 27.884553,17.8972881 27.884553,39.9722741 0,4.65651 -0.556376,9.127114 -1.578045,13.281436 -1.2654,0.257161 -2.559146,0.38954 -3.872974,0.38954 -4.694236,0 -8.31351,-1.408787 -12.445106,-3.601284 L 25.492126,61.663362 A 1.7289102,2.5254915 0 0 1 23.166595,58.713375 L 26.720461,37.731163 C 24.436178,31.874412 23.65618,25.205285 23.65618,18.326089 c 0,-5.002707 0.656939,-9.7835192 1.853432,-14.1818573 1.023289,-0.1630945 2.063377,-0.2490238 3.116744,-0.2490238 z"
     id="path3878"
     inkscape:connector-curvature="0"
     clip-path="url(#clipPath4277)" />
  <g
     id="g5083"
     transform="translate(0.35954582,1.2224558)">
    <circle
       r="1.7014307"
       id="path3047"
       style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
       cx="19.701431"
       cy="24.298569" />
    <circle
       r="1.7014307"
       id="path3047-9"
       style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
       cx="23.701431"
       cy="24.298569" />
    <circle
       r="1.7014307"
       id="path3047-5"
       style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
       cx="27.701431"
       cy="24.298569" />
  </g>
<svg xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    height="48" id="svg2" version="1.1"
    viewBox="0 0 48 48" width="48" xmlns="http://www.w3.org/2000/svg" inkscape:export-filename="/home/sam/Projects/OpenShareLocationPlugin/art/launcher.png"
    inkscape:export-xdpi="960"
    inkscape:export-ydpi="960"
    inkscape:version="0.91 r13725" sodipodi:docname="launcher.svg">
    <metadata id="metadata12">
        <rdf:RDF>
            <cc:Work rdf:about="">
                <dc:format>image/svg+xml</dc:format>
                <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
                <dc:title />
            </cc:Work>
        </rdf:RDF>
    </metadata>
    <defs id="defs10">
        <radialGradient cx="262.33273" cy="945.23846"
            fx="262.33273" fy="945.23846"
            gradientTransform="matrix(-0.17139995,-0.11076121,-0.06027526,0.1990256,151.7408,-109.89045)"
            gradientUnits="userSpaceOnUse" id="radialGradient3883" r="185.49754" inkscape:collect="always" xlink:href="#linearGradient3913" />
        <linearGradient id="linearGradient3913" inkscape:collect="always">
            <stop style="stop-color:#ffffff;stop-opacity:1;" id="stop3915" offset="0" />
            <stop style="stop-color:#ffffff;stop-opacity:0;" id="stop3917" offset="1" />
        </linearGradient>
        <clipPath clipPathUnits="userSpaceOnUse" id="clipPath4277">
            <path style="fill:#00a000;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:8, 8;stroke-dashoffset:1.19999993;stroke-opacity:0.50196078"
                d="M 23.999717,3.9995324 8.999718,40.589532 l 1.41,1.41 13.589999,-6 13.59,6 1.41,-1.41 z"
                id="path4279"
                inkscape:connector-curvature="0" />
        </clipPath>
        <clipPath clipPathUnits="userSpaceOnUse" id="clipPath4323">
            <path
                style="fill:#00a000;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:8, 8;stroke-dashoffset:1.19999993;stroke-opacity:0.50196078"
                d="M 24.008442,4.0723566 9.0084404,40.662356 l 1.4100006,1.41 13.590001,-6 13.589999,6 1.41,-1.41 z"
                id="path4325"
                inkscape:connector-curvature="0" />
        </clipPath>
    </defs>
    <sodipodi:namedview bordercolor="#666666" borderopacity="1" gridtolerance="10"
        guidetolerance="10" id="namedview8" objecttolerance="10" pagecolor="#ffffff"
        showgrid="false" inkscape:current-layer="svg2" inkscape:cx="6.8049159"
        inkscape:cy="29.859938" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-height="1017"
        inkscape:window-maximized="1" inkscape:window-width="1920" inkscape:window-x="0"
        inkscape:window-y="41" inkscape:zoom="9.8333331" />
    <path d="M0 0h48v48h-48z" fill="none" id="path4" />
    <path style="fill:#00a000;fill-opacity:1;stroke:#ffffff;stroke-opacity:0.50196081;stroke-linejoin:miter;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:8,8;stroke-dashoffset:1.19999993;stroke-linecap:butt" clip-path="url(#clipPath4323)"
        d="M24 4l-15 36.59 1.41 1.41 13.59-6 13.59 6 1.41-1.41z"
        id="path6" />
    <path
        style="display:inline;opacity:0.19211821;fill:url(#radialGradient3883);fill-opacity:1;stroke:none"
        clip-path="url(#clipPath4277)"
        d="m 28.626356,3.8952079 c 15.401473,0 27.884553,17.8972881 27.884553,39.9722741 0,4.65651 -0.556376,9.127114 -1.578045,13.281436 -1.2654,0.257161 -2.559146,0.38954 -3.872974,0.38954 -4.694236,0 -8.31351,-1.408787 -12.445106,-3.601284 L 25.492126,61.663362 A 1.7289102,2.5254915 0 0 1 23.166595,58.713375 L 26.720461,37.731163 C 24.436178,31.874412 23.65618,25.205285 23.65618,18.326089 c 0,-5.002707 0.656939,-9.7835192 1.853432,-14.1818573 1.023289,-0.1630945 2.063377,-0.2490238 3.116744,-0.2490238 z" id="path3878" inkscape:connector-curvature="0" />
    <g id="g5083" transform="translate(0.35954582,1.2224558)">
        <circle style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" cx="19.701431"
            cy="24.298569"
            id="path3047" r="1.7014307" />
        <circle style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" cx="23.701431"
            cy="24.298569"
            id="path3047-9" r="1.7014307" />
        <circle style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" cx="27.701431"
            cy="24.298569"
            id="path3047-5" r="1.7014307" />
    </g>
</svg>

M art/marker.svg => art/marker.svg +56 -108
@@ 1,110 1,58 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   width="48"
   height="48"
   viewBox="0 0 48 48"
   id="svg2"
   version="1.1"
   inkscape:version="0.91 r13725"
   sodipodi:docname="marker.svg">
  <metadata
     id="metadata10">
    <rdf:RDF>
      <cc:Work
         rdf:about="">
        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
        <dc:title />
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <defs
     id="defs8">
    <radialGradient
       inkscape:collect="always"
       xlink:href="#linearGradient3913"
       id="radialGradient3883"
       gradientUnits="userSpaceOnUse"
       gradientTransform="matrix(0.2039074,-0.09024614,0.07170697,0.16216229,-92.579229,-90.973095)"
       cx="262.33273"
       cy="945.23846"
       fx="262.33273"
       fy="945.23846"
       r="185.49754" />
    <linearGradient
       inkscape:collect="always"
       id="linearGradient3913">
      <stop
         style="stop-color:#ffffff;stop-opacity:1;"
         offset="0"
         id="stop3915" />
      <stop
         style="stop-color:#ffffff;stop-opacity:0;"
         offset="1"
         id="stop3917" />
    </linearGradient>
    <clipPath
       clipPathUnits="userSpaceOnUse"
       id="clipPath4167">
      <path
         inkscape:connector-curvature="0"
         d="M 24,4.0000001 C 16.27,4.0000001 10,10.27 10,18 10,28.5 24,44 24,44 24,44 38,28.5 38,18 38,10.27 31.73,4.0000001 24,4.0000001 Z M 24,23 c -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 0,2.76 -2.24,5 -5,5 z"
         id="path4169"
         style="fill:#000000;fill-opacity:1" />
    </clipPath>
    <clipPath
       clipPathUnits="userSpaceOnUse"
       id="clipPath4321">
      <path
         inkscape:connector-curvature="0"
         d="m 24,4.0001492 c -7.73,0 -14,6.2699998 -14,14.0000008 0,10.5 14,26 14,26 0,0 14,-15.5 14,-26 C 38,10.270149 31.73,4.0001492 24,4.0001492 Z M 24,23.00015 c -2.76,0 -5,-2.24 -5,-5 0,-2.760001 2.24,-5.000001 5,-5.000001 2.76,0 5,2.24 5,5.000001 0,2.76 -2.24,5 -5,5 z"
         id="path4323"
         style="fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:18, 3;stroke-dashoffset:0;stroke-opacity:0.53333285" />
    </clipPath>
  </defs>
  <sodipodi:namedview
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1"
     objecttolerance="10"
     gridtolerance="10"
     guidetolerance="10"
     inkscape:pageopacity="0"
     inkscape:pageshadow="2"
     inkscape:window-width="1920"
     inkscape:window-height="1010"
     id="namedview6"
     showgrid="false"
     inkscape:zoom="4.9166667"
     inkscape:cx="-15.254237"
     inkscape:cy="12.20339"
     inkscape:window-x="0"
     inkscape:window-y="41"
     inkscape:window-maximized="1"
     inkscape:current-layer="svg2" />
  <path
     d="M24 4c-7.73 0-14 6.27-14 14 0 10.5 14 26 14 26s14-15.5 14-26c0-7.73-6.27-14-14-14zm0 19c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"
     id="path4"
     style="fill:#00a000;fill-opacity:1;stroke:none;stroke-opacity:0.53333336;stroke-width:1.70000002;stroke-miterlimit:4;stroke-dasharray:none" />
  <path
     style="display:inline;opacity:0.19211821;fill:url(#radialGradient3883);fill-opacity:1;stroke:none"
     d="m 53.884912,1.7373006 c -18.322492,0 -33.173092,14.5823714 -33.173092,32.5686504 0,3.794038 0.661899,7.436601 1.877335,10.821463 1.505391,0.209531 3.044508,0.317391 4.607513,0.317391 5.584539,0 9.890238,-1.147853 14.805425,-2.934259 l 15.611481,6.295152 a 2.0568126,2.0577227 0 0 0 2.766588,-2.403594 l -4.227888,-17.09591 c 2.717518,-4.771967 3.645449,-10.205846 3.645449,-15.810885 0,-4.0761111 -0.781533,-7.9714274 -2.20495,-11.5551094 -1.217366,-0.132888 -2.454715,-0.202899 -3.707861,-0.202899 z"
     id="path3878"
     inkscape:connector-curvature="0"
     clip-path="url(#clipPath4167)" />
  <path
     inkscape:connector-curvature="0"
     d="M 24,4.0000003 C 16.27,4.0000003 10,10.27 10,18 10,28.5 24,44 24,44 24,44 38,28.5 38,18 38,10.27 31.73,4.0000003 24,4.0000003 Z M 24,23 c -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 0,2.76 -2.24,5 -5,5 z"
     id="path4-3"
     style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:30,5;stroke-opacity:0.53333336;stroke-dashoffset:44"
     clip-path="url(#clipPath4321)" />
<svg xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    height="48" id="svg2" version="1.1"
    viewBox="0 0 48 48" width="48" xmlns="http://www.w3.org/2000/svg" inkscape:version="0.91 r13725"
    sodipodi:docname="marker.svg">
    <metadata id="metadata10">
        <rdf:RDF>
            <cc:Work rdf:about="">
                <dc:format>image/svg+xml</dc:format>
                <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
                <dc:title />
            </cc:Work>
        </rdf:RDF>
    </metadata>
    <defs id="defs8">
        <radialGradient cx="262.33273" cy="945.23846"
            fx="262.33273" fy="945.23846"
            gradientTransform="matrix(0.2039074,-0.09024614,0.07170697,0.16216229,-92.579229,-90.973095)"
            gradientUnits="userSpaceOnUse" id="radialGradient3883" r="185.49754" inkscape:collect="always" xlink:href="#linearGradient3913" />
        <linearGradient id="linearGradient3913" inkscape:collect="always">
            <stop style="stop-color:#ffffff;stop-opacity:1;" id="stop3915" offset="0" />
            <stop style="stop-color:#ffffff;stop-opacity:0;" id="stop3917" offset="1" />
        </linearGradient>
        <clipPath clipPathUnits="userSpaceOnUse" id="clipPath4167">
            <path style="fill:#000000;fill-opacity:1"
                d="M 24,4.0000001 C 16.27,4.0000001 10,10.27 10,18 10,28.5 24,44 24,44 24,44 38,28.5 38,18 38,10.27 31.73,4.0000001 24,4.0000001 Z M 24,23 c -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 0,2.76 -2.24,5 -5,5 z"
                id="path4169" inkscape:connector-curvature="0" />
        </clipPath>
        <clipPath clipPathUnits="userSpaceOnUse" id="clipPath4321">
            <path style="fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:18, 3;stroke-dashoffset:0;stroke-opacity:0.53333285"
                d="m 24,4.0001492 c -7.73,0 -14,6.2699998 -14,14.0000008 0,10.5 14,26 14,26 0,0 14,-15.5 14,-26 C 38,10.270149 31.73,4.0001492 24,4.0001492 Z M 24,23.00015 c -2.76,0 -5,-2.24 -5,-5 0,-2.760001 2.24,-5.000001 5,-5.000001 2.76,0 5,2.24 5,5.000001 0,2.76 -2.24,5 -5,5 z"
                id="path4323"
                inkscape:connector-curvature="0" />
        </clipPath>
    </defs>
    <sodipodi:namedview bordercolor="#666666" borderopacity="1" gridtolerance="10"
        guidetolerance="10" id="namedview6" objecttolerance="10" pagecolor="#ffffff"
        showgrid="false" inkscape:current-layer="svg2" inkscape:cx="-15.254237"
        inkscape:cy="12.20339" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-height="1010"
        inkscape:window-maximized="1" inkscape:window-width="1920" inkscape:window-x="0"
        inkscape:window-y="41" inkscape:zoom="4.9166667" />
    <path
        style="fill:#00a000;fill-opacity:1;stroke:none;stroke-opacity:0.53333336;stroke-width:1.70000002;stroke-miterlimit:4;stroke-dasharray:none"
        d="M24 4c-7.73 0-14 6.27-14 14 0 10.5 14 26 14 26s14-15.5 14-26c0-7.73-6.27-14-14-14zm0 19c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"
        id="path4" />
    <path
        style="display:inline;opacity:0.19211821;fill:url(#radialGradient3883);fill-opacity:1;stroke:none"
        clip-path="url(#clipPath4167)"
        d="m 53.884912,1.7373006 c -18.322492,0 -33.173092,14.5823714 -33.173092,32.5686504 0,3.794038 0.661899,7.436601 1.877335,10.821463 1.505391,0.209531 3.044508,0.317391 4.607513,0.317391 5.584539,0 9.890238,-1.147853 14.805425,-2.934259 l 15.611481,6.295152 a 2.0568126,2.0577227 0 0 0 2.766588,-2.403594 l -4.227888,-17.09591 c 2.717518,-4.771967 3.645449,-10.205846 3.645449,-15.810885 0,-4.0761111 -0.781533,-7.9714274 -2.20495,-11.5551094 -1.217366,-0.132888 -2.454715,-0.202899 -3.707861,-0.202899 z" id="path3878" inkscape:connector-curvature="0" />
    <path style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:30,5;stroke-opacity:0.53333336;stroke-dashoffset:44"
        clip-path="url(#clipPath4321)"
        d="M 24,4.0000003 C 16.27,4.0000003 10,10.27 10,18 10,28.5 24,44 24,44 24,44 38,28.5 38,18 38,10.27 31.73,4.0000003 24,4.0000003 Z M 24,23 c -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 0,2.76 -2.24,5 -5,5 z"
        id="path4-3"
        inkscape:connector-curvature="0" />
</svg>

M build.gradle => build.gradle +1 -1
@@ 26,7 26,7 @@ android {
        targetSdkVersion 27
        versionCode 17
        versionName "2.4.2"
        manifestPlaceholders = [apiKey:thunderforestAPIKey]
        manifestPlaceholders = [apiKey: thunderforestAPIKey]
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8

D src/androidTest/java/com/samwhited/opensharelocationplugin/ApplicationTest.java => src/androidTest/java/com/samwhited/opensharelocationplugin/ApplicationTest.java +0 -13
@@ 1,13 0,0 @@
package com.samwhited.opensharelocationplugin;

import android.app.Application;
import android.test.ApplicationTestCase;

/**
 * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
 */
public class ApplicationTest extends ApplicationTestCase<Application> {
	public ApplicationTest() {
		super(Application.class);
	}
}
\ No newline at end of file

M src/main/java/com/samwhited/opensharelocationplugin/activities/AboutActivity.java => src/main/java/com/samwhited/opensharelocationplugin/activities/AboutActivity.java +21 -21
@@ 10,25 10,25 @@ import com.samwhited.opensharelocationplugin.R;

public class AboutActivity extends Activity {

	@Override
	protected void onCreate(final Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		final ActionBar actionBar = getActionBar();
		if (actionBar != null) {
			actionBar.setDisplayHomeAsUpEnabled(true);
		}

		setContentView(R.layout.activity_about);
	}

	@Override
	public boolean onOptionsItemSelected(final MenuItem item) {
		if (item.getItemId() == android.R.id.home) {
			finish();
			return true;
		}

		return super.onOptionsItemSelected(item);
	}
    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        final ActionBar actionBar = getActionBar();
        if (actionBar != null) {
            actionBar.setDisplayHomeAsUpEnabled(true);
        }

        setContentView(R.layout.activity_about);
    }

    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {
        if (item.getItemId() == android.R.id.home) {
            finish();
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

M src/main/java/com/samwhited/opensharelocationplugin/activities/LocationActivity.java => src/main/java/com/samwhited/opensharelocationplugin/activities/LocationActivity.java +296 -301
@@ 43,305 43,300 @@ import org.osmdroid.views.overlay.TilesOverlay;
import java.io.File;

public abstract class LocationActivity extends AppCompatActivity implements LocationListener {
	protected LocationManager locationManager;

	public static final String PREF_SHOW_PUBLIC_TRANSPORT = "pref_show_public_transport";

	public static final int REQUEST_CODE_CREATE = 0;
	public static final int REQUEST_CODE_FAB_PRESSED = 1;
	public static final int REQUEST_CODE_SNACKBAR_PRESSED = 2;

	protected static final String KEY_LOCATION = "loc";
	protected static final String KEY_ZOOM_LEVEL = "zoom";

	private TilesOverlay public_transport_overlay = null;
	protected Location myLoc = null;
	protected MapView map = null;
	protected IMapController mapController = null;

	protected Bitmap marker_icon;

	private void updateOverlays() {
		Log.d(Config.LOGTAG, "Updating overlays...");
		if (this.map == null) {
			Log.d(Config.LOGTAG, "No map found, failed to update overlays.");
			return;
		}

		if (getPreferences().getBoolean(LocationActivity.PREF_SHOW_PUBLIC_TRANSPORT, false)) {
			if (this.public_transport_overlay == null) {
				final MapTileProviderBasic tileProvider = new MapTileProviderBasic(getApplicationContext());
				tileProvider.setTileSource(Config.PUBLIC_TRANSPORT);
				this.public_transport_overlay = new TilesOverlay(tileProvider, getApplicationContext());
			}
			if (!map.getOverlays().contains(this.public_transport_overlay)) {
				map.getOverlays().add(this.public_transport_overlay);
			}
		} else if (map.getOverlays().contains(this.public_transport_overlay)){
			map.getOverlays().remove(this.public_transport_overlay);
		}

		map.invalidate();
	}

	protected void clearMarkers() {
		synchronized (this.map.getOverlays()) {
			for (final Overlay overlay : this.map.getOverlays()) {
				if (overlay instanceof Marker || overlay instanceof MyLocation) {
					this.map.getOverlays().remove(overlay);
				}
			}
		}
	}

	protected void updateLocationMarkers() {
		clearMarkers();
	}

	@Override
	protected void onCreate(final Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// Ask for location permissions if location services are enabled and we're just starting the activity
		// (we don't want to keep pestering them on every screen rotation or if there's no point because it's disabled
		// anyways).
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && savedInstanceState == null) {
			if (isLocationEnabled()) {
				requestPermissions(REQUEST_CODE_CREATE);
			}
		}

		final Context ctx = getApplicationContext();
		final IConfigurationProvider config = Configuration.getInstance();
		config.load(ctx, getPreferences());
		config.setUserAgentValue(BuildConfig.APPLICATION_ID + "_" + BuildConfig.VERSION_CODE);

		final File f = new File(getApplicationContext().getCacheDir()+ "/tiles");
		try {
			//noinspection ResultOfMethodCallIgnored
			f.mkdirs();
		} catch (final SecurityException ignored) {
		}
		if (f.exists() && f.isDirectory() && f.canRead() && f.canWrite()) {
			Log.d(Config.LOGTAG, "Setting tile cache at: " + f.getAbsolutePath());
			config.setOsmdroidTileCache(f.getAbsoluteFile());
		}

		this.locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
		this.marker_icon = BitmapFactory.decodeResource(ctx.getResources(), R.drawable.marker);
	}

	@Override
	protected void onSaveInstanceState(@NonNull final Bundle outState) {
		super.onSaveInstanceState(outState);

		final IGeoPoint center = map.getMapCenter();
		outState.putParcelable(KEY_LOCATION, new GeoPoint(
				center.getLatitude(),
				center.getLongitude()
		));
		outState.putDouble(KEY_ZOOM_LEVEL, map.getZoomLevelDouble());
	}

	@Override
	protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
		super.onRestoreInstanceState(savedInstanceState);

		if (savedInstanceState.containsKey(KEY_LOCATION)) {
			mapController.setCenter(savedInstanceState.getParcelable(KEY_LOCATION));
		}
		if (savedInstanceState.containsKey(KEY_ZOOM_LEVEL)) {
			mapController.setZoom(savedInstanceState.getDouble(KEY_ZOOM_LEVEL));
		}
	}

	protected void setupMapView(final GeoPoint pos) {
		// Get map view and configure it.
		map = findViewById(R.id.map);
		map.setTileSource(SettingsHelper.getTileProvider(getApplicationContext(), getPreferences().getString("tile_provider", "OPEN_STREET_MAP")));
		map.setBuiltInZoomControls(false);
		map.setMultiTouchControls(true);
		map.setTilesScaledToDpi(getPreferences().getBoolean("scale_tiles_for_high_dpi", false));
		this.mapController = map.getController();
		mapController.setZoom(Config.INITIAL_ZOOM_LEVEL);
		mapController.setCenter(pos);
		updateOverlays();
	}

	protected void gotoLoc() {
		gotoLoc(map.getZoomLevelDouble() == Config.INITIAL_ZOOM_LEVEL);
	}

	protected abstract void gotoLoc(final boolean setZoomLevel);

	protected abstract void setMyLoc(final Location location);

	protected void requestLocationUpdates() {
		Log.d(Config.LOGTAG, "Requesting location updates...");
		final Location lastKnownLocationGps;
		final Location lastKnownLocationNetwork;

		try {
			if (locationManager.getAllProviders().contains(LocationManager.GPS_PROVIDER)) {
				lastKnownLocationGps = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);

				if (lastKnownLocationGps != null) {
					setMyLoc(lastKnownLocationGps);
				}
				locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, Config.LOCATION_FIX_TIME_DELTA,
						Config.LOCATION_FIX_SPACE_DELTA, this);
			} else {
				lastKnownLocationGps = null;
			}

			if (locationManager.getAllProviders().contains(LocationManager.NETWORK_PROVIDER)) {
				lastKnownLocationNetwork = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
				if (lastKnownLocationNetwork != null && LocationHelper.isBetterLocation(lastKnownLocationNetwork,
						lastKnownLocationGps)) {
					setMyLoc(lastKnownLocationNetwork);
				}
				locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, Config.LOCATION_FIX_TIME_DELTA,
						Config.LOCATION_FIX_SPACE_DELTA, this);
			}

			// If something else is also querying for location more frequently than we are, the battery is already being
			// drained. Go ahead and use the existing locations as often as we can get them.
			if (locationManager.getAllProviders().contains(LocationManager.PASSIVE_PROVIDER)) {
				locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0, this);
			}
		} catch (final SecurityException ignored) {
			// This probably won't happen unless the user is on a ROM that allows permission tweaking.
			// TODO: Should we do anything if that is the case?
		}
	}

	protected void pauseLocationUpdates() throws SecurityException {
		locationManager.removeUpdates(this);
	}

	protected void setupMenuPrefs(final Menu menu) {
		final MenuItem show_public_transport = menu.findItem(R.id.action_show_public_transport);
		if (show_public_transport != null && show_public_transport.isCheckable()) {
			show_public_transport.setChecked(getPreferences().getBoolean(PREF_SHOW_PUBLIC_TRANSPORT, false));
		}
	}

	@Override
	public boolean onOptionsItemSelected(final MenuItem item) {
		switch (item.getItemId()) {
			case android.R.id.home:
				finish();
				return true;
			case R.id.action_show_public_transport:
				if (item.isCheckable()) {
					final boolean checked = !item.isChecked();
					item.setChecked(checked);
					getPreferences().edit().putBoolean(PREF_SHOW_PUBLIC_TRANSPORT, checked).apply();
				}
				updateOverlays();
				return true;
			case R.id.action_settings:
				startActivity(new Intent(this, SettingsActivity.class));
				return true;
		}
		return super.onOptionsItemSelected(item);
	}

	@Override
	protected void onPause() {
		super.onPause();
		Configuration.getInstance().save(this, getPreferences());
		map.onPause();
		try {
			pauseLocationUpdates();
		} catch (final SecurityException ignored) {
		}
	}

	protected abstract void updateUi();

	protected boolean mapAtInitialLoc() {
		return map.getZoomLevelDouble() == Config.INITIAL_ZOOM_LEVEL;
	}

	@Override
	protected void onResume() {
		super.onResume();
		Configuration.getInstance().load(this, getPreferences());
		map.onResume();
		this.setMyLoc(null);
		requestLocationUpdates();
		updateOverlays();
		updateLocationMarkers();
		updateUi();
		map.setTileSource(SettingsHelper.getTileProvider(getApplicationContext(), getPreferences().getString("tile_provider", "OPEN_STREET_MAP")));
		map.setTilesScaledToDpi(getPreferences().getBoolean("scale_tiles_for_high_dpi", false));

		if (mapAtInitialLoc()) {
			gotoLoc();
		}
	}

	@TargetApi(Build.VERSION_CODES.M)
	protected boolean hasLocationPermissions() {
		return (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
				checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED);
	}

	@TargetApi(Build.VERSION_CODES.M)
	protected void requestPermissions(final int request_code) {
		if (!hasLocationPermissions()) {
			requestPermissions(
					new String[]{
							Manifest.permission.ACCESS_FINE_LOCATION,
							Manifest.permission.ACCESS_COARSE_LOCATION,
					},
					request_code
			);
		}
	}

	@Override
	public void onRequestPermissionsResult(final int requestCode,
	                                       @NonNull final String[] permissions,
	                                       @NonNull final int[] grantResults) {
		super.onRequestPermissionsResult(requestCode, permissions, grantResults);
		for (int i = 0; i < grantResults.length; i++) {
			if (Manifest.permission.ACCESS_FINE_LOCATION.equals(permissions[i]) ||
					Manifest.permission.ACCESS_COARSE_LOCATION.equals(permissions[i])) {
				if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
					requestLocationUpdates();
				}
			}
		}
	}

	protected SharedPreferences getPreferences() {
		return PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
	}

	@TargetApi(Build.VERSION_CODES.KITKAT)
	private boolean isLocationEnabledKitkat() {
		try {
			final int locationMode = Settings.Secure.getInt(getContentResolver(), Settings.Secure.LOCATION_MODE);
			return locationMode != Settings.Secure.LOCATION_MODE_OFF;
		} catch( final Settings.SettingNotFoundException e ){
			return false;
		}
	}

	@SuppressWarnings("deprecation")
	private boolean isLocationEnabledLegacy() {
		final String locationProviders = Settings.Secure.getString(getContentResolver(),
				Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
		return !TextUtils.isEmpty(locationProviders);
	}

	protected boolean isLocationEnabled() {
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
			return isLocationEnabledKitkat();
		} else {
			return isLocationEnabledLegacy();
		}
	}
    public static final String PREF_SHOW_PUBLIC_TRANSPORT = "pref_show_public_transport";
    public static final int REQUEST_CODE_CREATE = 0;
    public static final int REQUEST_CODE_FAB_PRESSED = 1;
    public static final int REQUEST_CODE_SNACKBAR_PRESSED = 2;
    protected static final String KEY_LOCATION = "loc";
    protected static final String KEY_ZOOM_LEVEL = "zoom";
    protected LocationManager locationManager;
    protected Location myLoc = null;
    protected MapView map = null;
    protected IMapController mapController = null;
    protected Bitmap marker_icon;
    private TilesOverlay public_transport_overlay = null;

    private void updateOverlays() {
        Log.d(Config.LOGTAG, "Updating overlays...");
        if (this.map == null) {
            Log.d(Config.LOGTAG, "No map found, failed to update overlays.");
            return;
        }

        if (getPreferences().getBoolean(LocationActivity.PREF_SHOW_PUBLIC_TRANSPORT, false)) {
            if (this.public_transport_overlay == null) {
                final MapTileProviderBasic tileProvider = new MapTileProviderBasic(getApplicationContext());
                tileProvider.setTileSource(Config.PUBLIC_TRANSPORT);
                this.public_transport_overlay = new TilesOverlay(tileProvider, getApplicationContext());
            }
            if (!map.getOverlays().contains(this.public_transport_overlay)) {
                map.getOverlays().add(this.public_transport_overlay);
            }
        } else if (map.getOverlays().contains(this.public_transport_overlay)) {
            map.getOverlays().remove(this.public_transport_overlay);
        }

        map.invalidate();
    }

    protected void clearMarkers() {
        synchronized (this.map.getOverlays()) {
            for (final Overlay overlay : this.map.getOverlays()) {
                if (overlay instanceof Marker || overlay instanceof MyLocation) {
                    this.map.getOverlays().remove(overlay);
                }
            }
        }
    }

    protected void updateLocationMarkers() {
        clearMarkers();
    }

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Ask for location permissions if location services are enabled and we're just starting the activity
        // (we don't want to keep pestering them on every screen rotation or if there's no point because it's disabled
        // anyways).
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && savedInstanceState == null) {
            if (isLocationEnabled()) {
                requestPermissions(REQUEST_CODE_CREATE);
            }
        }

        final Context ctx = getApplicationContext();
        final IConfigurationProvider config = Configuration.getInstance();
        config.load(ctx, getPreferences());
        config.setUserAgentValue(BuildConfig.APPLICATION_ID + "_" + BuildConfig.VERSION_CODE);

        final File f = new File(getApplicationContext().getCacheDir() + "/tiles");
        try {
            //noinspection ResultOfMethodCallIgnored
            f.mkdirs();
        } catch (final SecurityException ignored) {
        }
        if (f.exists() && f.isDirectory() && f.canRead() && f.canWrite()) {
            Log.d(Config.LOGTAG, "Setting tile cache at: " + f.getAbsolutePath());
            config.setOsmdroidTileCache(f.getAbsoluteFile());
        }

        this.locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
        this.marker_icon = BitmapFactory.decodeResource(ctx.getResources(), R.drawable.marker);
    }

    @Override
    protected void onSaveInstanceState(@NonNull final Bundle outState) {
        super.onSaveInstanceState(outState);

        final IGeoPoint center = map.getMapCenter();
        outState.putParcelable(KEY_LOCATION, new GeoPoint(
                center.getLatitude(),
                center.getLongitude()
        ));
        outState.putDouble(KEY_ZOOM_LEVEL, map.getZoomLevelDouble());
    }

    @Override
    protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        if (savedInstanceState.containsKey(KEY_LOCATION)) {
            mapController.setCenter(savedInstanceState.getParcelable(KEY_LOCATION));
        }
        if (savedInstanceState.containsKey(KEY_ZOOM_LEVEL)) {
            mapController.setZoom(savedInstanceState.getDouble(KEY_ZOOM_LEVEL));
        }
    }

    protected void setupMapView(final GeoPoint pos) {
        // Get map view and configure it.
        map = findViewById(R.id.map);
        map.setTileSource(SettingsHelper.getTileProvider(getApplicationContext(), getPreferences().getString("tile_provider", "OPEN_STREET_MAP")));
        map.setBuiltInZoomControls(false);
        map.setMultiTouchControls(true);
        map.setTilesScaledToDpi(getPreferences().getBoolean("scale_tiles_for_high_dpi", false));
        this.mapController = map.getController();
        mapController.setZoom(Config.INITIAL_ZOOM_LEVEL);
        mapController.setCenter(pos);
        updateOverlays();
    }

    protected void gotoLoc() {
        gotoLoc(map.getZoomLevelDouble() == Config.INITIAL_ZOOM_LEVEL);
    }

    protected abstract void gotoLoc(final boolean setZoomLevel);

    protected abstract void setMyLoc(final Location location);

    protected void requestLocationUpdates() {
        Log.d(Config.LOGTAG, "Requesting location updates...");
        final Location lastKnownLocationGps;
        final Location lastKnownLocationNetwork;

        try {
            if (locationManager.getAllProviders().contains(LocationManager.GPS_PROVIDER)) {
                lastKnownLocationGps = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);

                if (lastKnownLocationGps != null) {
                    setMyLoc(lastKnownLocationGps);
                }
                locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, Config.LOCATION_FIX_TIME_DELTA,
                        Config.LOCATION_FIX_SPACE_DELTA, this);
            } else {
                lastKnownLocationGps = null;
            }

            if (locationManager.getAllProviders().contains(LocationManager.NETWORK_PROVIDER)) {
                lastKnownLocationNetwork = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
                if (lastKnownLocationNetwork != null && LocationHelper.isBetterLocation(lastKnownLocationNetwork,
                        lastKnownLocationGps)) {
                    setMyLoc(lastKnownLocationNetwork);
                }
                locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, Config.LOCATION_FIX_TIME_DELTA,
                        Config.LOCATION_FIX_SPACE_DELTA, this);
            }

            // If something else is also querying for location more frequently than we are, the battery is already being
            // drained. Go ahead and use the existing locations as often as we can get them.
            if (locationManager.getAllProviders().contains(LocationManager.PASSIVE_PROVIDER)) {
                locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0, this);
            }
        } catch (final SecurityException ignored) {
            // This probably won't happen unless the user is on a ROM that allows permission tweaking.
            // TODO: Should we do anything if that is the case?
        }
    }

    protected void pauseLocationUpdates() throws SecurityException {
        locationManager.removeUpdates(this);
    }

    protected void setupMenuPrefs(final Menu menu) {
        final MenuItem show_public_transport = menu.findItem(R.id.action_show_public_transport);
        if (show_public_transport != null && show_public_transport.isCheckable()) {
            show_public_transport.setChecked(getPreferences().getBoolean(PREF_SHOW_PUBLIC_TRANSPORT, false));
        }
    }

    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                finish();
                return true;
            case R.id.action_show_public_transport:
                if (item.isCheckable()) {
                    final boolean checked = !item.isChecked();
                    item.setChecked(checked);
                    getPreferences().edit().putBoolean(PREF_SHOW_PUBLIC_TRANSPORT, checked).apply();
                }
                updateOverlays();
                return true;
            case R.id.action_settings:
                startActivity(new Intent(this, SettingsActivity.class));
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onPause() {
        super.onPause();
        Configuration.getInstance().save(this, getPreferences());
        map.onPause();
        try {
            pauseLocationUpdates();
        } catch (final SecurityException ignored) {
        }
    }

    protected abstract void updateUi();

    protected boolean mapAtInitialLoc() {
        return map.getZoomLevelDouble() == Config.INITIAL_ZOOM_LEVEL;
    }

    @Override
    protected void onResume() {
        super.onResume();
        Configuration.getInstance().load(this, getPreferences());
        map.onResume();
        this.setMyLoc(null);
        requestLocationUpdates();
        updateOverlays();
        updateLocationMarkers();
        updateUi();
        map.setTileSource(SettingsHelper.getTileProvider(getApplicationContext(), getPreferences().getString("tile_provider", "OPEN_STREET_MAP")));
        map.setTilesScaledToDpi(getPreferences().getBoolean("scale_tiles_for_high_dpi", false));

        if (mapAtInitialLoc()) {
            gotoLoc();
        }
    }

    @TargetApi(Build.VERSION_CODES.M)
    protected boolean hasLocationPermissions() {
        return (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
                checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED);
    }

    @TargetApi(Build.VERSION_CODES.M)
    protected void requestPermissions(final int request_code) {
        if (!hasLocationPermissions()) {
            requestPermissions(
                    new String[]{
                            Manifest.permission.ACCESS_FINE_LOCATION,
                            Manifest.permission.ACCESS_COARSE_LOCATION,
                    },
                    request_code
            );
        }
    }

    @Override
    public void onRequestPermissionsResult(final int requestCode,
                                           @NonNull final String[] permissions,
                                           @NonNull final int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        for (int i = 0; i < grantResults.length; i++) {
            if (Manifest.permission.ACCESS_FINE_LOCATION.equals(permissions[i]) ||
                    Manifest.permission.ACCESS_COARSE_LOCATION.equals(permissions[i])) {
                if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                    requestLocationUpdates();
                }
            }
        }
    }

    protected SharedPreferences getPreferences() {
        return PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
    }

    @TargetApi(Build.VERSION_CODES.KITKAT)
    private boolean isLocationEnabledKitkat() {
        try {
            final int locationMode = Settings.Secure.getInt(getContentResolver(), Settings.Secure.LOCATION_MODE);
            return locationMode != Settings.Secure.LOCATION_MODE_OFF;
        } catch (final Settings.SettingNotFoundException e) {
            return false;
        }
    }

    @SuppressWarnings("deprecation")
    private boolean isLocationEnabledLegacy() {
        final String locationProviders = Settings.Secure.getString(getContentResolver(),
                Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
        return !TextUtils.isEmpty(locationProviders);
    }

    protected boolean isLocationEnabled() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            return isLocationEnabledKitkat();
        } else {
            return isLocationEnabledLegacy();
        }
    }
}

M src/main/java/com/samwhited/opensharelocationplugin/activities/SettingsActivity.java => src/main/java/com/samwhited/opensharelocationplugin/activities/SettingsActivity.java +15 -15
@@ 10,20 10,20 @@ import com.samwhited.opensharelocationplugin.fragments.SettingsFragment;

public class SettingsActivity extends Activity {

	@Override
	protected void onCreate(final Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		final FragmentManager fm = getFragmentManager();
		SettingsFragment mSettingsFragment = (SettingsFragment) fm.findFragmentById(android.R.id.content);
		if (mSettingsFragment == null || !mSettingsFragment.getClass().equals(SettingsFragment.class)) {
			mSettingsFragment = new SettingsFragment();
			fm.beginTransaction().replace(android.R.id.content, mSettingsFragment).commit();
		}
	}
    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final FragmentManager fm = getFragmentManager();
        SettingsFragment mSettingsFragment = (SettingsFragment) fm.findFragmentById(android.R.id.content);
        if (mSettingsFragment == null || !mSettingsFragment.getClass().equals(SettingsFragment.class)) {
            mSettingsFragment = new SettingsFragment();
            fm.beginTransaction().replace(android.R.id.content, mSettingsFragment).commit();
        }
    }

	@Override
	protected void onStart() {
		super.onStart();
		PreferenceManager.setDefaultValues(getApplicationContext(), R.xml.settings, false);
	}
    @Override
    protected void onStart() {
        super.onStart();
        PreferenceManager.setDefaultValues(getApplicationContext(), R.xml.settings, false);
    }
}

M src/main/java/com/samwhited/opensharelocationplugin/activities/ShareLocationActivity.java => src/main/java/com/samwhited/opensharelocationplugin/activities/ShareLocationActivity.java +261 -262
@@ 27,266 27,265 @@ import org.osmdroid.util.GeoPoint;

public class ShareLocationActivity extends LocationActivity implements LocationListener {

	private RelativeLayout snackBar;
	private boolean marker_fixed_to_loc = false;
	private MenuItem toggle_fixed_location_item;

	private static final String KEY_FIXED_TO_LOC = "fixed_to_loc";
	private Boolean noAskAgain = false;

	@Override
	protected void onSaveInstanceState(@NonNull final Bundle outState) {
		super.onSaveInstanceState(outState);

		outState.putBoolean(KEY_FIXED_TO_LOC, marker_fixed_to_loc);
	}

	@Override
	protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
		super.onRestoreInstanceState(savedInstanceState);

		if (savedInstanceState.containsKey(KEY_FIXED_TO_LOC)) {
			this.marker_fixed_to_loc = savedInstanceState.getBoolean(KEY_FIXED_TO_LOC);
		}
	}

	@Override
	protected void onCreate(final Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		setContentView(R.layout.activity_share_location);
		setupMapView(Config.INITIAL_POS);

		// Setup the cancel button
		final Button cancelButton = findViewById(R.id.cancel_button);
		cancelButton.setOnClickListener(view -> {
			setResult(RESULT_CANCELED);
			finish();
		});

		// Setup the snackbar
		this.snackBar = findViewById(R.id.snackbar);
		final TextView snackbarAction = findViewById(R.id.snackbar_action);
		snackbarAction.setOnClickListener(view -> {
			if (isLocationEnabledAndAllowed()) {
				updateUi();
			} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !hasLocationPermissions()) {
				requestPermissions(REQUEST_CODE_SNACKBAR_PRESSED);
			} else if (!isLocationEnabled()) {
				startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
			}
		});

		// Setup the share button
		final Button shareButton = findViewById(R.id.share_button);
		if (shareButton != null) {
			shareButton.setOnClickListener(view -> {
				final Intent result = new Intent();

				if (marker_fixed_to_loc && myLoc != null) {
					result.putExtra("latitude", myLoc.getLatitude());
					result.putExtra("longitude", myLoc.getLongitude());
					result.putExtra("altitude", myLoc.getAltitude());
					result.putExtra("accuracy", (int) myLoc.getAccuracy());
				} else {
					final IGeoPoint markerPoint = map.getMapCenter();
					result.putExtra("latitude", markerPoint.getLatitude());
					result.putExtra("longitude", markerPoint.getLongitude());
				}

				setResult(RESULT_OK, result);
				finish();
			});
		}

		this.marker_fixed_to_loc = isLocationEnabledAndAllowed();

		// Setup the fab button on v21+ devices
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
			final ImageButton toggleFixedMarkerButton = findViewById(R.id.toggle_fixed_marker_button);
			toggleFixedMarkerButton.setOnClickListener(view -> {
				if (!marker_fixed_to_loc) {
					if (!isLocationEnabled()) {
						startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
					} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
						requestPermissions(REQUEST_CODE_FAB_PRESSED);
					}
				}
				toggleFixedLocation();
			});
		}
	}

	@Override
	public void onRequestPermissionsResult(
			final int requestCode,
			@NonNull final String[] permissions,
			@NonNull final int[] grantResults
	) {
		super.onRequestPermissionsResult(requestCode, permissions, grantResults);

		if (grantResults.length > 0 &&
				grantResults[0] != PackageManager.PERMISSION_GRANTED &&
				Build.VERSION.SDK_INT >= 23 &&
				permissions.length > 0 &&
				(
						Manifest.permission.LOCATION_HARDWARE.equals(permissions[0]) ||
								Manifest.permission.ACCESS_FINE_LOCATION.equals(permissions[0]) ||
								Manifest.permission.ACCESS_COARSE_LOCATION.equals(permissions[0])
				) &&
				!shouldShowRequestPermissionRationale(permissions[0])) {
			noAskAgain = true;
		}

		if (!noAskAgain && requestCode == REQUEST_CODE_SNACKBAR_PRESSED && !isLocationEnabled() && hasLocationPermissions()) {
			startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
		}
		updateUi();
	}

	@Override
	protected void gotoLoc(final boolean setZoomLevel) {
		if (this.myLoc != null && mapController != null) {
			if (setZoomLevel) {
				mapController.setZoom(Config.FINAL_ZOOM_LEVEL);
			}
			mapController.animateTo(new GeoPoint(this.myLoc));
		}
	}

	@Override
	protected void setMyLoc(final Location location) {
		this.myLoc = location;
	}

	@Override
	protected void onPause() {
		super.onPause();
	}

	@Override
	protected void updateLocationMarkers() {
		super.updateLocationMarkers();
		if (this.myLoc != null) {
			this.map.getOverlays().add(new MyLocation(this, null, this.myLoc));
			if (this.marker_fixed_to_loc) {
				map.getOverlays().add(new Marker(marker_icon, new GeoPoint(this.myLoc)));
			} else {
				map.getOverlays().add(new Marker(marker_icon));
			}
		} else {
			map.getOverlays().add(new Marker(marker_icon));
		}
	}

	@Override
	public void onLocationChanged(final Location location) {
		if (this.myLoc == null) {
			this.marker_fixed_to_loc = true;
		}
		updateUi();
		if (LocationHelper.isBetterLocation(location, this.myLoc)) {
			final Location oldLoc = this.myLoc;
			this.myLoc = location;

			// Don't jump back to the users location if they're not moving (more or less).
			if (oldLoc == null || (this.marker_fixed_to_loc && this.myLoc.distanceTo(oldLoc) > 1)) {
				gotoLoc();
			}

			updateLocationMarkers();
		}
	}

	@Override
	public void onStatusChanged(final String provider, final int status, final Bundle extras) {

	}

	@Override
	public void onProviderEnabled(final String provider) {

	}

	@Override
	public void onProviderDisabled(final String provider) {

	}

	private boolean isLocationEnabledAndAllowed() {
		return (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || hasLocationPermissions()) && isLocationEnabled();
	}

	@Override
	public boolean onCreateOptionsMenu(final Menu menu) {
		getMenuInflater().inflate(R.menu.menu_share_location, menu);
		super.setupMenuPrefs(menu);
		this.toggle_fixed_location_item = menu.findItem(R.id.toggle_fixed_marker_button);
		updateUi();
		return true;
	}

	private void toggleFixedLocation() {
		this.marker_fixed_to_loc = isLocationEnabledAndAllowed() && !this.marker_fixed_to_loc;
		if (this.marker_fixed_to_loc) {
			gotoLoc(false);
		}
		updateLocationMarkers();
		updateUi();
	}

	@Override
	protected void updateUi() {
		if (noAskAgain || isLocationEnabledAndAllowed()) {
			this.snackBar.setVisibility(View.GONE);
		} else {
			this.snackBar.setVisibility(View.VISIBLE);
		}
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
			// Setup the fab button on v21+ devices
			final ImageButton fab = findViewById(R.id.toggle_fixed_marker_button);
			if (isLocationEnabledAndAllowed()) {
				fab.setVisibility(View.VISIBLE);
				runOnUiThread(() -> {
					fab.setImageResource(marker_fixed_to_loc ? R.drawable.ic_gps_fixed_white_24dp :
							R.drawable.ic_gps_not_fixed_white_24dp);
					fab.setContentDescription(getResources().getString(
								marker_fixed_to_loc ? R.string.action_unfix_from_location : R.string.action_fix_to_location
								));
					fab.invalidate();
				});
			} else {
				fab.setVisibility(View.GONE);
			}
		} else {
			// Setup the action bar button on < v21 devices
			if (isLocationEnabledAndAllowed()) {
				if (toggle_fixed_location_item != null) {
					toggle_fixed_location_item.setVisible(true);
					runOnUiThread(() -> {
						toggle_fixed_location_item.setIcon(marker_fixed_to_loc ?
								R.drawable.ic_gps_fixed_white_24dp : R.drawable.ic_gps_not_fixed_white_24dp
								);
						toggle_fixed_location_item.setTitle(marker_fixed_to_loc ?
								R.string.action_unfix_from_location : R.string.action_fix_to_location
								);
					});
				}
			} else {
				if (toggle_fixed_location_item != null) {
					toggle_fixed_location_item.setVisible(false);
				}
			}
		}
	}

	@Override
	public boolean onOptionsItemSelected(final MenuItem item) {
		switch (item.getItemId()) {
			case R.id.toggle_fixed_marker_button:
				toggleFixedLocation();
				return true;
		}
		return super.onOptionsItemSelected(item);
	}
    private static final String KEY_FIXED_TO_LOC = "fixed_to_loc";
    private RelativeLayout snackBar;
    private boolean marker_fixed_to_loc = false;
    private MenuItem toggle_fixed_location_item;
    private Boolean noAskAgain = false;

    @Override
    protected void onSaveInstanceState(@NonNull final Bundle outState) {
        super.onSaveInstanceState(outState);

        outState.putBoolean(KEY_FIXED_TO_LOC, marker_fixed_to_loc);
    }

    @Override
    protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        if (savedInstanceState.containsKey(KEY_FIXED_TO_LOC)) {
            this.marker_fixed_to_loc = savedInstanceState.getBoolean(KEY_FIXED_TO_LOC);
        }
    }

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_share_location);
        setupMapView(Config.INITIAL_POS);

        // Setup the cancel button
        final Button cancelButton = findViewById(R.id.cancel_button);
        cancelButton.setOnClickListener(view -> {
            setResult(RESULT_CANCELED);
            finish();
        });

        // Setup the snackbar
        this.snackBar = findViewById(R.id.snackbar);
        final TextView snackbarAction = findViewById(R.id.snackbar_action);
        snackbarAction.setOnClickListener(view -> {
            if (isLocationEnabledAndAllowed()) {
                updateUi();
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !hasLocationPermissions()) {
                requestPermissions(REQUEST_CODE_SNACKBAR_PRESSED);
            } else if (!isLocationEnabled()) {
                startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
            }
        });

        // Setup the share button
        final Button shareButton = findViewById(R.id.share_button);
        if (shareButton != null) {
            shareButton.setOnClickListener(view -> {
                final Intent result = new Intent();

                if (marker_fixed_to_loc && myLoc != null) {
                    result.putExtra("latitude", myLoc.getLatitude());
                    result.putExtra("longitude", myLoc.getLongitude());
                    result.putExtra("altitude", myLoc.getAltitude());
                    result.putExtra("accuracy", (int) myLoc.getAccuracy());
                } else {
                    final IGeoPoint markerPoint = map.getMapCenter();
                    result.putExtra("latitude", markerPoint.getLatitude());
                    result.putExtra("longitude", markerPoint.getLongitude());
                }

                setResult(RESULT_OK, result);
                finish();
            });
        }

        this.marker_fixed_to_loc = isLocationEnabledAndAllowed();

        // Setup the fab button on v21+ devices
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            final ImageButton toggleFixedMarkerButton = findViewById(R.id.toggle_fixed_marker_button);
            toggleFixedMarkerButton.setOnClickListener(view -> {
                if (!marker_fixed_to_loc) {
                    if (!isLocationEnabled()) {
                        startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
                    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        requestPermissions(REQUEST_CODE_FAB_PRESSED);
                    }
                }
                toggleFixedLocation();
            });
        }
    }

    @Override
    public void onRequestPermissionsResult(
            final int requestCode,
            @NonNull final String[] permissions,
            @NonNull final int[] grantResults
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        if (grantResults.length > 0 &&
                grantResults[0] != PackageManager.PERMISSION_GRANTED &&
                Build.VERSION.SDK_INT >= 23 &&
                permissions.length > 0 &&
                (
                        Manifest.permission.LOCATION_HARDWARE.equals(permissions[0]) ||
                                Manifest.permission.ACCESS_FINE_LOCATION.equals(permissions[0]) ||
                                Manifest.permission.ACCESS_COARSE_LOCATION.equals(permissions[0])
                ) &&
                !shouldShowRequestPermissionRationale(permissions[0])) {
            noAskAgain = true;
        }

        if (!noAskAgain && requestCode == REQUEST_CODE_SNACKBAR_PRESSED && !isLocationEnabled() && hasLocationPermissions()) {
            startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
        }
        updateUi();
    }

    @Override
    protected void gotoLoc(final boolean setZoomLevel) {
        if (this.myLoc != null && mapController != null) {
            if (setZoomLevel) {
                mapController.setZoom(Config.FINAL_ZOOM_LEVEL);
            }
            mapController.animateTo(new GeoPoint(this.myLoc));
        }
    }

    @Override
    protected void setMyLoc(final Location location) {
        this.myLoc = location;
    }

    @Override
    protected void onPause() {
        super.onPause();
    }

    @Override
    protected void updateLocationMarkers() {
        super.updateLocationMarkers();
        if (this.myLoc != null) {
            this.map.getOverlays().add(new MyLocation(this, null, this.myLoc));
            if (this.marker_fixed_to_loc) {
                map.getOverlays().add(new Marker(marker_icon, new GeoPoint(this.myLoc)));
            } else {
                map.getOverlays().add(new Marker(marker_icon));
            }
        } else {
            map.getOverlays().add(new Marker(marker_icon));
        }
    }

    @Override
    public void onLocationChanged(final Location location) {
        if (this.myLoc == null) {
            this.marker_fixed_to_loc = true;
        }
        updateUi();
        if (LocationHelper.isBetterLocation(location, this.myLoc)) {
            final Location oldLoc = this.myLoc;
            this.myLoc = location;

            // Don't jump back to the users location if they're not moving (more or less).
            if (oldLoc == null || (this.marker_fixed_to_loc && this.myLoc.distanceTo(oldLoc) > 1)) {
                gotoLoc();
            }

            updateLocationMarkers();
        }
    }

    @Override
    public void onStatusChanged(final String provider, final int status, final Bundle extras) {

    }

    @Override
    public void onProviderEnabled(final String provider) {

    }

    @Override
    public void onProviderDisabled(final String provider) {

    }

    private boolean isLocationEnabledAndAllowed() {
        return (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || hasLocationPermissions()) && isLocationEnabled();
    }

    @Override
    public boolean onCreateOptionsMenu(final Menu menu) {
        getMenuInflater().inflate(R.menu.menu_share_location, menu);
        super.setupMenuPrefs(menu);
        this.toggle_fixed_location_item = menu.findItem(R.id.toggle_fixed_marker_button);
        updateUi();
        return true;
    }

    private void toggleFixedLocation() {
        this.marker_fixed_to_loc = isLocationEnabledAndAllowed() && !this.marker_fixed_to_loc;
        if (this.marker_fixed_to_loc) {
            gotoLoc(false);
        }
        updateLocationMarkers();
        updateUi();
    }

    @Override
    protected void updateUi() {
        if (noAskAgain || isLocationEnabledAndAllowed()) {
            this.snackBar.setVisibility(View.GONE);
        } else {
            this.snackBar.setVisibility(View.VISIBLE);
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            // Setup the fab button on v21+ devices
            final ImageButton fab = findViewById(R.id.toggle_fixed_marker_button);
            if (isLocationEnabledAndAllowed()) {
                fab.setVisibility(View.VISIBLE);
                runOnUiThread(() -> {
                    fab.setImageResource(marker_fixed_to_loc ? R.drawable.ic_gps_fixed_white_24dp :
                            R.drawable.ic_gps_not_fixed_white_24dp);
                    fab.setContentDescription(getResources().getString(
                            marker_fixed_to_loc ? R.string.action_unfix_from_location : R.string.action_fix_to_location
                    ));
                    fab.invalidate();
                });
            } else {
                fab.setVisibility(View.GONE);
            }
        } else {
            // Setup the action bar button on < v21 devices
            if (isLocationEnabledAndAllowed()) {
                if (toggle_fixed_location_item != null) {
                    toggle_fixed_location_item.setVisible(true);
                    runOnUiThread(() -> {
                        toggle_fixed_location_item.setIcon(marker_fixed_to_loc ?
                                R.drawable.ic_gps_fixed_white_24dp : R.drawable.ic_gps_not_fixed_white_24dp
                        );
                        toggle_fixed_location_item.setTitle(marker_fixed_to_loc ?
                                R.string.action_unfix_from_location : R.string.action_fix_to_location
                        );
                    });
                }
            } else {
                if (toggle_fixed_location_item != null) {
                    toggle_fixed_location_item.setVisible(false);
                }
            }
        }
    }

    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {
        switch (item.getItemId()) {
            case R.id.toggle_fixed_marker_button:
                toggleFixedLocation();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

M src/main/java/com/samwhited/opensharelocationplugin/fragments/SettingsFragment.java => src/main/java/com/samwhited/opensharelocationplugin/fragments/SettingsFragment.java +5 -5
@@ 7,10 7,10 @@ import com.samwhited.opensharelocationplugin.R;

public class SettingsFragment extends PreferenceFragment {

	@Override
	public void onCreate(final Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
    @Override
    public void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

		addPreferencesFromResource(R.xml.settings);
	}
        addPreferencesFromResource(R.xml.settings);
    }
}

M src/main/java/com/samwhited/opensharelocationplugin/overlays/Marker.java => src/main/java/com/samwhited/opensharelocationplugin/overlays/Marker.java +39 -37
@@ 12,41 12,43 @@ import org.osmdroid.views.overlay.mylocation.SimpleLocationOverlay;
 * An immutable marker overlay.
 */
public class Marker extends SimpleLocationOverlay {
	private final GeoPoint position;
	private final Bitmap icon;
	private final Point mapPoint;

	/**
	 * Create a marker overlay which will be drawn at the current Geographical position.
	 * @param icon A bitmap icon for the marker
	 * @param position The geographic position where the marker will be drawn (if it is inside the view)
	 */
	public Marker(final Bitmap icon, final GeoPoint position) {
		super(icon);
		this.icon = icon;
		this.position = position;
		this.mapPoint = new Point();
	}

	/**
	 * Create a marker overlay which will be drawn centered in the view.
	 * @param icon A bitmap icon for the marker
	 */
	public Marker(final Bitmap icon) {
		this(icon, null);
	}

	@Override
	public void draw(final Canvas c, final MapView view, final boolean shadow) {
		super.draw(c, view, shadow);

		// If no position was set for the marker, draw it centered in the view.
		view.getProjection().toPixels(this.position == null ? view.getMapCenter() : position, mapPoint);

		c.drawBitmap(icon,
				mapPoint.x - icon.getWidth() / 2,
				mapPoint.y - icon.getHeight(),
				null);

	}
    private final GeoPoint position;
    private final Bitmap icon;
    private final Point mapPoint;

    /**
     * Create a marker overlay which will be drawn at the current Geographical position.
     *
     * @param icon     A bitmap icon for the marker
     * @param position The geographic position where the marker will be drawn (if it is inside the view)
     */
    public Marker(final Bitmap icon, final GeoPoint position) {
        super(icon);
        this.icon = icon;
        this.position = position;
        this.mapPoint = new Point();
    }

    /**
     * Create a marker overlay which will be drawn centered in the view.
     *
     * @param icon A bitmap icon for the marker
     */
    public Marker(final Bitmap icon) {
        this(icon, null);
    }

    @Override
    public void draw(final Canvas c, final MapView view, final boolean shadow) {
        super.draw(c, view, shadow);

        // If no position was set for the marker, draw it centered in the view.
        view.getProjection().toPixels(this.position == null ? view.getMapCenter() : position, mapPoint);

        c.drawBitmap(icon,
                mapPoint.x - icon.getWidth() / 2,
                mapPoint.y - icon.getHeight(),
                null);

    }
}

M src/main/java/com/samwhited/opensharelocationplugin/overlays/MyLocation.java => src/main/java/com/samwhited/opensharelocationplugin/overlays/MyLocation.java +40 -40
@@ 19,48 19,48 @@ import org.osmdroid.views.overlay.mylocation.SimpleLocationOverlay;
import microsoft.mappoint.TileSystem;

public class MyLocation extends SimpleLocationOverlay {
	private final GeoPoint position;
	private final float accuracy;
	private final Point mapCenterPoint;
	private final Paint fill;
	private final Paint outline;
    private final GeoPoint position;
    private final float accuracy;
    private final Point mapCenterPoint;
    private final Paint fill;
    private final Paint outline;

	@TargetApi(Build.VERSION_CODES.LOLLIPOP)
	private int getColor(final Context ctx) {
		final int accent;
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
			accent = ctx.getResources().getColor(R.color.accent, ctx.getTheme());
		} else {
			//noinspection deprecation
			accent = ctx.getResources().getColor(R.color.accent);
		}
		return accent;
	}
    public MyLocation(final Context ctx, final Bitmap icon, final Location position) {
        super(icon);
        this.mapCenterPoint = new Point();
        this.fill = new Paint(Paint.ANTI_ALIAS_FLAG);
        final int accent = this.getColor(ctx);
        fill.setColor(accent);
        fill.setStyle(Paint.Style.FILL);
        this.outline = new Paint(Paint.ANTI_ALIAS_FLAG);
        outline.setColor(accent);
        outline.setAlpha(50);
        outline.setStyle(Paint.Style.FILL);
        this.position = new GeoPoint(position);
        this.accuracy = position.getAccuracy();
    }

	public MyLocation(final Context ctx, final Bitmap icon, final Location position) {
		super(icon);
		this.mapCenterPoint = new Point();
		this.fill = new Paint(Paint.ANTI_ALIAS_FLAG);
		final int accent = this.getColor(ctx);
		fill.setColor(accent);
		fill.setStyle(Paint.Style.FILL);
		this.outline = new Paint(Paint.ANTI_ALIAS_FLAG);
		outline.setColor(accent);
		outline.setAlpha(50);
		outline.setStyle(Paint.Style.FILL);
		this.position = new GeoPoint(position);
		this.accuracy = position.getAccuracy();
	}
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private int getColor(final Context ctx) {
        final int accent;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            accent = ctx.getResources().getColor(R.color.accent, ctx.getTheme());
        } else {
            //noinspection deprecation
            accent = ctx.getResources().getColor(R.color.accent);
        }
        return accent;
    }

	@Override
	public void draw(final Canvas c, final MapView view, final boolean shadow) {
		super.draw(c, view, shadow);
    @Override
    public void draw(final Canvas c, final MapView view, final boolean shadow) {
        super.draw(c, view, shadow);

		view.getProjection().toPixels(position, mapCenterPoint);
		c.drawCircle(mapCenterPoint.x, mapCenterPoint.y,
				Math.max(Config.MY_LOCATION_INDICATOR_SIZE + Config.MY_LOCATION_INDICATOR_OUTLINE_SIZE,
						accuracy / (float) TileSystem.GroundResolution(position.getLatitude(), view.getZoomLevel())
				), this.outline);
		c.drawCircle(mapCenterPoint.x, mapCenterPoint.y, Config.MY_LOCATION_INDICATOR_SIZE, this.fill);
	}
        view.getProjection().toPixels(position, mapCenterPoint);
        c.drawCircle(mapCenterPoint.x, mapCenterPoint.y,
                Math.max(Config.MY_LOCATION_INDICATOR_SIZE + Config.MY_LOCATION_INDICATOR_OUTLINE_SIZE,
                        accuracy / (float) TileSystem.GroundResolution(position.getLatitude(), view.getZoomLevel())
                ), this.outline);
        c.drawCircle(mapCenterPoint.x, mapCenterPoint.y, Config.MY_LOCATION_INDICATOR_SIZE, this.fill);
    }
}

M src/main/java/com/samwhited/opensharelocationplugin/preferences/AboutPreference.java => src/main/java/com/samwhited/opensharelocationplugin/preferences/AboutPreference.java +36 -36
@@ 10,41 10,41 @@ import com.samwhited.opensharelocationplugin.R;
import com.samwhited.opensharelocationplugin.activities.AboutActivity;

public class AboutPreference extends Preference {
	@SuppressWarnings("unused")
	public AboutPreference(final Context context, final AttributeSet attrs, final int defStyle) {
		super(context, attrs, defStyle);
		setSummary();
	}

	@SuppressWarnings("unused")
	public AboutPreference(final Context context, final AttributeSet attrs) {
		super(context, attrs);
		setSummary();
	}

	@Override
	protected void onClick() {
		super.onClick();
		final Intent intent = new Intent(getContext(), AboutActivity.class);
		getContext().startActivity(intent);
	}

	public String getVersion() {
		final Context context = getContext();
		final String packageName = context == null ? null : context.getPackageName();
		if (packageName != null) {
			try {
				return context.getPackageManager().getPackageInfo(packageName, 0).versionName;
			} catch (final PackageManager.NameNotFoundException e) {
				return "";
			}
		} else {
			return "";
		}
	}

	private void setSummary() {
		setSummary(getContext().getResources().getString(R.string.app_name) + " " + getVersion());
	}
    @SuppressWarnings("unused")
    public AboutPreference(final Context context, final AttributeSet attrs, final int defStyle) {
        super(context, attrs, defStyle);
        setSummary();
    }

    @SuppressWarnings("unused")
    public AboutPreference(final Context context, final AttributeSet attrs) {
        super(context, attrs);
        setSummary();
    }

    @Override
    protected void onClick() {
        super.onClick();
        final Intent intent = new Intent(getContext(), AboutActivity.class);
        getContext().startActivity(intent);
    }

    public String getVersion() {
        final Context context = getContext();
        final String packageName = context == null ? null : context.getPackageName();
        if (packageName != null) {
            try {
                return context.getPackageManager().getPackageInfo(packageName, 0).versionName;
            } catch (final PackageManager.NameNotFoundException e) {
                return "";
            }
        } else {
            return "";
        }
    }

    private void setSummary() {
        setSummary(getContext().getResources().getString(R.string.app_name) + " " + getVersion());
    }
}


M src/main/java/com/samwhited/opensharelocationplugin/util/Config.java => src/main/java/com/samwhited/opensharelocationplugin/util/Config.java +12 -13
@@ 1,21 1,20 @@
package com.samwhited.opensharelocationplugin.util;

import org.osmdroid.tileprovider.tilesource.OnlineTileSourceBase;
import org.osmdroid.tileprovider.tilesource.TileSourceFactory;
import org.osmdroid.tileprovider.tilesource.XYTileSource;
import org.osmdroid.util.GeoPoint;

public final class Config {
	public final static String LOGTAG = "oslp";
	public final static double INITIAL_ZOOM_LEVEL = 4;
	public final static double FINAL_ZOOM_LEVEL = 15;
	public final static GeoPoint INITIAL_POS = new GeoPoint(33.805278, -84.171389);
	public final static int MY_LOCATION_INDICATOR_SIZE = 10;
	public final static int MY_LOCATION_INDICATOR_OUTLINE_SIZE = 3;
	public final static long LOCATION_FIX_TIME_DELTA = 1000 * 10; // ms
	public final static float LOCATION_FIX_SPACE_DELTA = 10; // m
	final static int LOCATION_FIX_SIGNIFICANT_TIME_DELTA = 1000 * 60 * 2; // ms
	public static final OnlineTileSourceBase PUBLIC_TRANSPORT = new XYTileSource(
			"PublicTransport", 0, 17, 256, ".png",
			new String[] { "http://www.openptmap.org/tiles/" },"Data © OpenStreetMap contributors.");
    public final static String LOGTAG = "oslp";
    public final static double INITIAL_ZOOM_LEVEL = 4;
    public final static double FINAL_ZOOM_LEVEL = 15;
    public final static GeoPoint INITIAL_POS = new GeoPoint(33.805278, -84.171389);
    public final static int MY_LOCATION_INDICATOR_SIZE = 10;
    public final static int MY_LOCATION_INDICATOR_OUTLINE_SIZE = 3;
    public final static long LOCATION_FIX_TIME_DELTA = 1000 * 10; // ms
    public final static float LOCATION_FIX_SPACE_DELTA = 10; // m
    public static final OnlineTileSourceBase PUBLIC_TRANSPORT = new XYTileSource(
            "PublicTransport", 0, 17, 256, ".png",
            new String[]{"http://www.openptmap.org/tiles/"}, "Data © OpenStreetMap contributors.");
    final static int LOCATION_FIX_SIGNIFICANT_TIME_DELTA = 1000 * 60 * 2; // ms
}

M src/main/java/com/samwhited/opensharelocationplugin/util/LocationHelper.java => src/main/java/com/samwhited/opensharelocationplugin/util/LocationHelper.java +54 -54
@@ 5,66 5,66 @@ import android.location.Location;
import org.osmdroid.util.GeoPoint;

public final class LocationHelper {
	/**
	 * Parses a lat long string in the form "lat,long".
	 *
	 * @param latlong A string in the form "lat,long"
	 * @return A GeoPoint representing the lat,long string.
	 * @throws NumberFormatException If an invalid lat or long is specified.
	 */
	public static GeoPoint parseLatLong(final String latlong) throws NumberFormatException {
		if (latlong == null || latlong.isEmpty()) {
			return null;
		}
    /**
     * Parses a lat long string in the form "lat,long".
     *
     * @param latlong A string in the form "lat,long"
     * @return A GeoPoint representing the lat,long string.
     * @throws NumberFormatException If an invalid lat or long is specified.
     */
    public static GeoPoint parseLatLong(final String latlong) throws NumberFormatException {
        if (latlong == null || latlong.isEmpty()) {
            return null;
        }

		final String[] parts = latlong.split(",");
		if (parts[1].contains("?")) {
			parts[1] = parts[1].substring(0, parts[1].indexOf("?"));
		}
		return new GeoPoint(Double.valueOf(parts[0]), Double.valueOf(parts[1]));
	}
        final String[] parts = latlong.split(",");
        if (parts[1].contains("?")) {
            parts[1] = parts[1].substring(0, parts[1].indexOf("?"));
        }
        return new GeoPoint(Double.valueOf(parts[0]), Double.valueOf(parts[1]));
    }

	private static boolean isSameProvider(final String provider1, final String provider2) {
		if (provider1 == null) {
			return provider2 == null;
		}
		return provider1.equals(provider2);
	}
    private static boolean isSameProvider(final String provider1, final String provider2) {
        if (provider1 == null) {
            return provider2 == null;
        }
        return provider1.equals(provider2);
    }

	public static boolean isBetterLocation(final Location location, final Location prevLoc) {
		if (prevLoc == null) {
			return true;
		}
    public static boolean isBetterLocation(final Location location, final Location prevLoc) {
        if (prevLoc == null) {
            return true;
        }

		// Check whether the new location fix is newer or older
		final long timeDelta = location.getTime() - prevLoc.getTime();
		final boolean isSignificantlyNewer = timeDelta > Config.LOCATION_FIX_SIGNIFICANT_TIME_DELTA;
		final boolean isSignificantlyOlder = timeDelta < -Config.LOCATION_FIX_SIGNIFICANT_TIME_DELTA;
		final boolean isNewer = timeDelta > 0;
        // Check whether the new location fix is newer or older
        final long timeDelta = location.getTime() - prevLoc.getTime();
        final boolean isSignificantlyNewer = timeDelta > Config.LOCATION_FIX_SIGNIFICANT_TIME_DELTA;
        final boolean isSignificantlyOlder = timeDelta < -Config.LOCATION_FIX_SIGNIFICANT_TIME_DELTA;
        final boolean isNewer = timeDelta > 0;

		if (isSignificantlyNewer) {
			return true;
		} else if (isSignificantlyOlder) {
			return false;
		}
        if (isSignificantlyNewer) {
            return true;
        } else if (isSignificantlyOlder) {
            return false;
        }

		// Check whether the new location fix is more or less accurate
		final int accuracyDelta = (int) (location.getAccuracy() - prevLoc.getAccuracy());
		final boolean isLessAccurate = accuracyDelta > 0;
		final boolean isMoreAccurate = accuracyDelta < 0;
		final boolean isSignificantlyLessAccurate = accuracyDelta > 200;
        // Check whether the new location fix is more or less accurate
        final int accuracyDelta = (int) (location.getAccuracy() - prevLoc.getAccuracy());
        final boolean isLessAccurate = accuracyDelta > 0;
        final boolean isMoreAccurate = accuracyDelta < 0;
        final boolean isSignificantlyLessAccurate = accuracyDelta > 200;

		// Check if the old and new location are from the same provider
		final boolean isFromSameProvider = isSameProvider(location.getProvider(), prevLoc.getProvider());
        // Check if the old and new location are from the same provider
        final boolean isFromSameProvider = isSameProvider(location.getProvider(), prevLoc.getProvider());

		// Determine location quality using a combination of timeliness and accuracy
		if (isMoreAccurate) {
			return true;
		} else if (isNewer && !isLessAccurate) {
			return true;
		} else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
			return true;
		}
		return false;
	}
        // Determine location quality using a combination of timeliness and accuracy
        if (isMoreAccurate) {
            return true;
        } else if (isNewer && !isLessAccurate) {
            return true;
        } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
            return true;
        }
        return false;
    }
}

M src/main/java/com/samwhited/opensharelocationplugin/util/SettingsHelper.java => src/main/java/com/samwhited/opensharelocationplugin/util/SettingsHelper.java +15 -15
@@ 8,19 8,19 @@ import org.osmdroid.tileprovider.tilesource.TileSourceFactory;
import org.osmdroid.tileprovider.tilesource.XYTileSource;

public final class SettingsHelper {
	public static OnlineTileSourceBase getTileProvider(final Context ctx, final String provider_name) {
		switch (provider_name) {
			case "CYCLEMAP":
				return new ThunderforestTileSource(ctx, ThunderforestTileSource.CYCLE);
			case "TOPOMAP":
				return TileSourceFactory.OpenTopo;
			case "OPEN_STREET_MAP":
			default:
				return new XYTileSource("OpenStreetMap",
						0, 19, 256, ".png", new String[] {
							"https://a.tile.openstreetmap.org/",
							"https://b.tile.openstreetmap.org/",
							"https://c.tile.openstreetmap.org/" },"© OpenStreetMap contributors");
		}
	}
    public static OnlineTileSourceBase getTileProvider(final Context ctx, final String provider_name) {
        switch (provider_name) {
            case "CYCLEMAP":
                return new ThunderforestTileSource(ctx, ThunderforestTileSource.CYCLE);
            case "TOPOMAP":
                return TileSourceFactory.OpenTopo;
            case "OPEN_STREET_MAP":
            default:
                return new XYTileSource("OpenStreetMap",
                        0, 19, 256, ".png", new String[]{
                        "https://a.tile.openstreetmap.org/",
                        "https://b.tile.openstreetmap.org/",
                        "https://c.tile.openstreetmap.org/"}, "© OpenStreetMap contributors");
        }
    }
}

M src/main/java/com/samwhited/opensharelocationplugin/util/UriHelper.java => src/main/java/com/samwhited/opensharelocationplugin/util/UriHelper.java +19 -19
@@ 7,25 7,25 @@ import java.util.HashMap;
 */
public final class UriHelper {

	/**
	 * Parses a query string into a hashmap.
	 *
	 * @param q The query string to split.
	 * @return A hashmap containing the key-value pairs from the query string.
	 */
	public static HashMap<String, String> parseQueryString(final String q) {
		if (q == null || q.isEmpty()) {
			return null;
		}
    /**
     * Parses a query string into a hashmap.
     *
     * @param q The query string to split.
     * @return A hashmap containing the key-value pairs from the query string.
     */
    public static HashMap<String, String> parseQueryString(final String q) {
        if (q == null || q.isEmpty()) {
            return null;
        }

		final String[] query = q.split("&");
		// TODO: Look up the HashMap implementation and figure out what the load factor is and make sure we're not reallocating here.
		final HashMap<String, String> queryMap = new HashMap<>(query.length);
		for (final String param : query) {
			final String[] pair = param.split("=");
			queryMap.put(pair[0], pair.length == 2 && !pair[1].isEmpty() ? pair[1] : null);
		}
        final String[] query = q.split("&");
        // TODO: Look up the HashMap implementation and figure out what the load factor is and make sure we're not reallocating here.
        final HashMap<String, String> queryMap = new HashMap<>(query.length);
        for (final String param : query) {
            final String[] pair = param.split("=");
            queryMap.put(pair[0], pair.length == 2 && !pair[1].isEmpty() ? pair[1] : null);
        }

		return queryMap;
	}
        return queryMap;
    }
}

M src/main/res/drawable/snackbar.xml => src/main/res/drawable/snackbar.xml +2 -3
@@ 1,8 1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
<?xml version="1.0" encoding="UTF-8"?><!--
Resource copied from Conversations and the official Location sharing plugin by iNPUTmice.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<shape xmlns:android="http://schemas.android.com/apk/res/android">

    <solid android:color="@color/darkbackground" />


M src/main/res/layout-v21/activity_share_location.xml => src/main/res/layout-v21/activity_share_location.xml +24 -21
@@ 8,20 8,22 @@
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_above="@id/button_bar">
    <org.osmdroid.views.MapView android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/toggle_fixed_marker_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:src="@drawable/ic_gps_fixed_white_24dp"
        android:tint="@android:color/white"
        android:layout_margin="16dp"
        android:background="@color/accent"
        android:contentDescription="@string/action_unfix_from_location"/>
        <org.osmdroid.views.MapView
            android:id="@+id/map"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <android.support.design.widget.FloatingActionButton
            android:id="@+id/toggle_fixed_marker_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="end|bottom"
            android:layout_margin="16dp"
            android:background="@color/accent"
            android:contentDescription="@string/action_unfix_from_location"
            android:src="@drawable/ic_gps_fixed_white_24dp"
            android:tint="@android:color/white" />
    </FrameLayout>

    <!-- Parts of this resource are copied from the official location sharing plugin by iNPUTmice -->


@@ 29,11 31,11 @@
        android:id="@+id/snackbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="4dp"
        android:layout_above="@+id/button_bar"
        android:layout_marginBottom="4dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_above="@+id/button_bar"
        android:layout_marginTop="4dp"
        android:background="@drawable/snackbar"
        android:minHeight="48dp"
        android:visibility="visible">


@@ 45,25 47,26 @@
            android:layout_centerVertical="true"
            android:layout_toStartOf="@+id/snackbar_action"
            android:paddingStart="24dp"
            android:text="@string/location_disabled"
            android:textColor="@color/ondarktext"
            android:textSize="?attr/TextSizeBody"
            android:text="@string/location_disabled"
            tools:ignore="RtlSymmetry"/>
            tools:ignore="RtlSymmetry" />

        <TextView
            android:id="@+id/snackbar_action"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:layout_alignParentTop="true"
            android:paddingBottom="16dp"
            android:paddingLeft="24dp"
            android:paddingRight="24dp"
            android:paddingTop="16dp"
            android:text="@string/enable"
            android:textAllCaps="true"
            android:textColor="@color/ondarktext"
            android:textSize="?attr/TextSizeBody"
            android:textStyle="bold"
            android:text="@string/enable"
            android:layout_alignParentTop="true"
            android:layout_alignParentEnd="true"/>
            android:textStyle="bold" />
    </RelativeLayout>

    <LinearLayout

M src/main/res/layout-v21/activity_show_location.xml => src/main/res/layout-v21/activity_show_location.xml +10 -7
@@ 4,19 4,22 @@
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.samwhited.opensharelocationplugin.activities.ShowLocationActivity">
    <org.osmdroid.views.MapView android:id="@+id/map"

    <org.osmdroid.views.MapView
        android:id="@+id/map"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"/>
        android:layout_height="fill_parent" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/action_directions"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_alignParentEnd="true"
        android:layout_alignParentBottom="true"
        android:src="@drawable/ic_directions_white_24dp"
        android:tint="@android:color/white"
        android:layout_alignParentEnd="true"
        android:layout_gravity="end|bottom"
        android:layout_margin="16dp"
        android:background="@color/accent"
        android:contentDescription="@string/action_directions"/>
        android:contentDescription="@string/action_directions"
        android:src="@drawable/ic_directions_white_24dp"
        android:tint="@android:color/white" />
</RelativeLayout>
\ No newline at end of file

M src/main/res/layout/activity_about.xml => src/main/res/layout/activity_about.xml +8 -8
@@ 1,18 1,18 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                tools:context="com.samwhited.opensharelocationplugin.activities.AboutActivity">
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.samwhited.opensharelocationplugin.activities.AboutActivity">

    <TextView
        android:text="@string/about_text"
        android:autoLink="all"
        android:typeface="monospace"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/activity_horizontal_margin"
        android:layout_marginRight="@dimen/activity_horizontal_margin"
        android:layout_marginTop="@dimen/activity_vertical_margin"
        android:paddingBottom="@dimen/activity_vertical_margin"/>
        android:autoLink="all"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:text="@string/about_text"
        android:typeface="monospace" />

</ScrollView>

M src/main/res/layout/activity_share_location.xml => src/main/res/layout/activity_share_location.xml +17 -16
@@ 1,24 1,25 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                tools:context="com.samwhited.opensharelocationplugin.activities.ShareLocationActivity">
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.samwhited.opensharelocationplugin.activities.ShareLocationActivity">

    <org.osmdroid.views.MapView android:id="@+id/map"
                                android:layout_width="match_parent"
                                android:layout_height="match_parent"
                                android:layout_above="@+id/button_bar" />
    <org.osmdroid.views.MapView
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/button_bar" />

    <!-- Parts of this resource are copied from the official location sharing plugin by iNPUTmice -->
    <RelativeLayout
        android:id="@+id/snackbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="4dp"
        android:layout_above="@+id/button_bar"
        android:layout_marginBottom="4dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_above="@+id/button_bar"
        android:layout_marginTop="4dp"
        android:background="@drawable/snackbar"
        android:minHeight="48dp"
        android:visibility="visible">


@@ 30,26 31,26 @@
            android:layout_centerVertical="true"
            android:layout_toStartOf="@+id/snackbar_action"
            android:paddingStart="24dp"
            android:text="@string/location_disabled"
            android:textColor="@color/ondarktext"
            android:textSize="?attr/TextSizeBody"
            android:text="@string/location_disabled"
            tools:ignore="RtlSymmetry"/>
            tools:ignore="RtlSymmetry" />

        <TextView
            android:id="@+id/snackbar_action"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:layout_alignParentTop="true"
            android:paddingBottom="16dp"
            android:paddingLeft="24dp"
            android:paddingRight="24dp"
            android:paddingTop="16dp"
            android:text="@string/enable"
            android:textAllCaps="true"
            android:textColor="@color/ondarktext"
            android:textSize="?attr/TextSizeBody"
            android:textStyle="bold"
            android:text="@string/enable"
            android:layout_alignParentTop="true"
            android:layout_alignParentEnd="true"/>
            android:textStyle="bold" />
    </RelativeLayout>

    <LinearLayout

M src/main/res/layout/activity_show_location.xml => src/main/res/layout/activity_show_location.xml +9 -7
@@ 1,10 1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                tools:context="com.samwhited.opensharelocationplugin.activities.ShowLocationActivity">
    <org.osmdroid.views.MapView android:id="@+id/map"
                                android:layout_width="fill_parent"
                                android:layout_height="fill_parent"/>
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.samwhited.opensharelocationplugin.activities.ShowLocationActivity">

    <org.osmdroid.views.MapView
        android:id="@+id/map"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />
</RelativeLayout>
\ No newline at end of file

M src/main/res/menu-v21/menu_share_location.xml => src/main/res/menu-v21/menu_share_location.xml +18 -16
@@ 1,19 1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:tools="http://schemas.android.com/tools"
      xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <item android:id="@+id/action_show_public_transport"
          android:title="@string/action_show_public_transport"
          android:checkable="true"
          android:checked="false"
          app:showAsAction="never"
          android:showAsAction="never"
          tools:ignore="AppCompatResource"/>
    <item android:id="@+id/action_settings"
          android:title="@string/action_settings"
          android:orderInCategory="100"
          app:showAsAction="never"
          android:showAsAction="never"
          tools:ignore="AppCompatResource"/>
    <item
        android:id="@+id/action_show_public_transport"
        android:checkable="true"
        android:checked="false"
        android:showAsAction="never"
        android:title="@string/action_show_public_transport"
        app:showAsAction="never"
        tools:ignore="AppCompatResource" />
    <item
        android:id="@+id/action_settings"
        android:orderInCategory="100"
        android:showAsAction="never"
        android:title="@string/action_settings"
        app:showAsAction="never"
        tools:ignore="AppCompatResource" />
</menu>

M src/main/res/menu-v21/menu_show_location.xml => src/main/res/menu-v21/menu_show_location.xml +32 -28
@@ 1,29 1,33 @@
<menu xmlns:tools="http://schemas.android.com/tools"
      xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
    <item android:id="@+id/action_share_location"
          app:showAsAction="ifRoom"
          android:showAsAction="ifRoom"
          android:title="@string/action_share_location"
          android:actionProviderClass="android.widget.ShareActionProvider"
          tools:ignore="AppCompatResource"/>
    <item android:id="@+id/action_copy_location"
          android:title="@string/action_copy_location"
          android:icon="@drawable/ic_content_copy_white_24dp"
          app:showAsAction="ifRoom"
          android:showAsAction="ifRoom"
          tools:ignore="AppCompatResource"/>
    <item android:id="@+id/action_show_public_transport"
          android:title="@string/action_show_public_transport"
          android:checkable="true"
          android:checked="false"
          app:showAsAction="never"
          android:showAsAction="never"
          tools:ignore="AppCompatResource"/>
    <item android:id="@+id/action_settings"
          android:title="@string/action_settings"
          android:orderInCategory="100"
          app:showAsAction="never"
          android:showAsAction="never"
          tools:ignore="AppCompatResource"/>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
    <item
        android:id="@+id/action_share_location"
        android:actionProviderClass="android.widget.ShareActionProvider"
        android:showAsAction="ifRoom"
        android:title="@string/action_share_location"
        app:showAsAction="ifRoom"
        tools:ignore="AppCompatResource" />
    <item
        android:id="@+id/action_copy_location"
        android:icon="@drawable/ic_content_copy_white_24dp"
        android:showAsAction="ifRoom"
        android:title="@string/action_copy_location"
        app:showAsAction="ifRoom"
        tools:ignore="AppCompatResource" />
    <item
        android:id="@+id/action_show_public_transport"
        android:checkable="true"
        android:checked="false"
        android:showAsAction="never"
        android:title="@string/action_show_public_transport"
        app:showAsAction="never"
        tools:ignore="AppCompatResource" />
    <item
        android:id="@+id/action_settings"
        android:orderInCategory="100"
        android:showAsAction="never"
        android:title="@string/action_settings"
        app:showAsAction="never"
        tools:ignore="AppCompatResource" />
</menu>

M src/main/res/menu/menu_share_location.xml => src/main/res/menu/menu_share_location.xml +24 -21
@@ 1,24 1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:tools="http://schemas.android.com/tools"
      xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

      <item android:id="@+id/toggle_fixed_marker_button"
            android:title="@string/action_unfix_from_location"
            android:icon="@drawable/ic_gps_fixed_white_24dp"
            android:showAsAction="ifRoom"
            tools:ignore="AppCompatResource"/>
      <item android:id="@+id/action_show_public_transport"
            android:title="@string/action_show_public_transport"
            android:checkable="true"
            android:checked="false"
            app:showAsAction="never"
            android:showAsAction="never"
            tools:ignore="AppCompatResource"/>
      <item android:id="@+id/action_settings"
            android:title="@string/action_settings"
            android:orderInCategory="100"
            app:showAsAction="never"
            android:showAsAction="never"
            tools:ignore="AppCompatResource"/>
    <item
        android:id="@+id/toggle_fixed_marker_button"
        android:icon="@drawable/ic_gps_fixed_white_24dp"
        android:showAsAction="ifRoom"
        android:title="@string/action_unfix_from_location"
        tools:ignore="AppCompatResource" />
    <item
        android:id="@+id/action_show_public_transport"
        android:checkable="true"
        android:checked="false"
        android:showAsAction="never"
        android:title="@string/action_show_public_transport"
        app:showAsAction="never"
        tools:ignore="AppCompatResource" />
    <item
        android:id="@+id/action_settings"
        android:orderInCategory="100"
        android:showAsAction="never"
        android:title="@string/action_settings"
        app:showAsAction="never"
        tools:ignore="AppCompatResource" />
</menu>

M src/main/res/menu/menu_show_location.xml => src/main/res/menu/menu_show_location.xml +39 -34
@@ 1,35 1,40 @@
<menu xmlns:tools="http://schemas.android.com/tools"
      xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
    <item android:id="@+id/action_share_location"
          app:showAsAction="ifRoom"
          android:showAsAction="ifRoom"
          android:title="@string/action_share_location"
          android:actionProviderClass="android.widget.ShareActionProvider"
          tools:ignore="AppCompatResource"/>
    <item android:id="@+id/action_directions"
          android:title="@string/action_directions"
          android:icon="@drawable/ic_directions_white_24dp"
          app:showAsAction="ifRoom"
          android:showAsAction="ifRoom"
          tools:ignore="AppCompatResource"/>
    <item android:id="@+id/action_copy_location"
          android:title="@string/action_copy_location"
          android:icon="@drawable/ic_content_copy_white_24dp"
          app:showAsAction="ifRoom"
          android:showAsAction="ifRoom"
          tools:ignore="AppCompatResource"/>
    <item android:id="@+id/action_show_public_transport"
          android:title="@string/action_show_public_transport"
          android:checkable="true"
          android:checked="false"
          app:showAsAction="never"
          android:showAsAction="never"
          tools:ignore="AppCompatResource"/>
    <item android:id="@+id/action_settings"
          android:title="@string/action_settings"
          android:orderInCategory="100"
          app:showAsAction="never"
          android:showAsAction="never"
          tools:ignore="AppCompatResource"/>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
    <item
        android:id="@+id/action_share_location"
        android:actionProviderClass="android.widget.ShareActionProvider"
        android:showAsAction="ifRoom"
        android:title="@string/action_share_location"
        app:showAsAction="ifRoom"
        tools:ignore="AppCompatResource" />
    <item
        android:id="@+id/action_directions"
        android:icon="@drawable/ic_directions_white_24dp"
        android:showAsAction="ifRoom"
        android:title="@string/action_directions"
        app:showAsAction="ifRoom"
        tools:ignore="AppCompatResource" />
    <item
        android:id="@+id/action_copy_location"
        android:icon="@drawable/ic_content_copy_white_24dp"
        android:showAsAction="ifRoom"
        android:title="@string/action_copy_location"
        app:showAsAction="ifRoom"
        tools:ignore="AppCompatResource" />
    <item
        android:id="@+id/action_show_public_transport"
        android:checkable="true"
        android:checked="false"
        android:showAsAction="never"
        android:title="@string/action_show_public_transport"
        app:showAsAction="never"
        tools:ignore="AppCompatResource" />
    <item
        android:id="@+id/action_settings"
        android:orderInCategory="100"
        android:showAsAction="never"
        android:title="@string/action_settings"
        app:showAsAction="never"
        tools:ignore="AppCompatResource" />
</menu>

M src/main/res/values-de/strings.xml => src/main/res/values-de/strings.xml +18 -18
@@ 1,23 1,23 @@
<resources>
	<string name="title_activity_share_location">Standort freigeben</string>
	<string name="title_activity_show_location">Standort zeigen</string>
	<string name="title_activity_about">Über</string>
	<string name="title_activity_settings">Einstellungen</string>
    <string name="title_activity_share_location">Standort freigeben</string>
    <string name="title_activity_show_location">Standort zeigen</string>
    <string name="title_activity_about">Über</string>
    <string name="title_activity_settings">Einstellungen</string>

	<string name="action_copy_location">Standort kopieren</string>
	<string name="action_share_location">Standort teilen</string>
	<string name="action_fix_to_location">Position fixieren</string>
	<string name="action_unfix_from_location">Position lösen</string>
	<string name="action_show_public_transport">ÖPNV anzeigen</string>
	<string name="action_directions">Navigation</string>
	<string name="action_settings">Einstellungen</string>
    <string name="action_copy_location">Standort kopieren</string>
    <string name="action_share_location">Standort teilen</string>
    <string name="action_fix_to_location">Position fixieren</string>
    <string name="action_unfix_from_location">Position lösen</string>
    <string name="action_show_public_transport">ÖPNV anzeigen</string>
    <string name="action_directions">Navigation</string>
    <string name="action_settings">Einstellungen</string>

	<string name="location_disabled">Standort ist deaktiviert</string>
	<string name="enable">Aktivieren</string>
	<string name="cancel">Abbruch</string>
	<string name="share">Freigeben</string>
    <string name="location_disabled">Standort ist deaktiviert</string>
    <string name="enable">Aktivieren</string>
    <string name="cancel">Abbruch</string>
    <string name="share">Freigeben</string>

	<string name="pref_tile_provider">Kartenanbieter</string>
	<string name="pref_scale_tiles">Kacheln skalieren</string>
	<string name="pref_scale_tiles_summary">Kacheln vergrößern für Sehschwäche oder Bildschirme mit hoher DPI</string>
    <string name="pref_tile_provider">Kartenanbieter</string>
    <string name="pref_scale_tiles">Kacheln skalieren</string>
    <string name="pref_scale_tiles_summary">Kacheln vergrößern für Sehschwäche oder Bildschirme mit hoher DPI</string>
</resources>

M src/main/res/values-fr/strings.xml => src/main/res/values-fr/strings.xml +18 -18
@@ 1,23 1,23 @@
<resources>
	<string name="title_activity_share_location">Partager localisation</string>
	<string name="title_activity_show_location">Montrer localisation</string>
	<string name="title_activity_about">À propos de l’application</string>
	<string name="title_activity_settings">Paramètres</string>
    <string name="title_activity_share_location">Partager localisation</string>
    <string name="title_activity_show_location">Montrer localisation</string>
    <string name="title_activity_about">À propos de l’application</string>
    <string name="title_activity_settings">Paramètres</string>

	<string name="action_copy_location">Copier localisation</string>
	<string name="action_share_location">Partager localisation</string>
	<string name="action_fix_to_location">Fixer localisation</string>
	<string name="action_unfix_from_location">Détacher localisation</string>
	<string name="action_show_public_transport">Montrer transports en communs</string>
	<string name="action_directions">Navigation</string>
	<string name="action_settings">Paramètres</string>
    <string name="action_copy_location">Copier localisation</string>
    <string name="action_share_location">Partager localisation</string>
    <string name="action_fix_to_location">Fixer localisation</string>
    <string name="action_unfix_from_location">Détacher localisation</string>
    <string name="action_show_public_transport">Montrer transports en communs</string>
    <string name="action_directions">Navigation</string>
    <string name="action_settings">Paramètres</string>

	<string name="location_disabled">La localisation est désactivée</string>
	<string name="enable">Activer</string>
	<string name="cancel">Annuler</string>
	<string name="share">Partager</string>
    <string name="location_disabled">La localisation est désactivée</string>
    <string name="enable">Activer</string>
    <string name="cancel">Annuler</string>
    <string name="share">Partager</string>

	<string name="pref_tile_provider">Fournisseur de cartes</string>
	<string name="pref_scale_tiles">Élargir les tuiles</string>
	<string name="pref_scale_tiles_summary">Élargir les tuiles pour utilisateurs malvoyants ou écrans à haut DPI (PPP)</string>
    <string name="pref_tile_provider">Fournisseur de cartes</string>
    <string name="pref_scale_tiles">Élargir les tuiles</string>
    <string name="pref_scale_tiles_summary">Élargir les tuiles pour utilisateurs malvoyants ou écrans à haut DPI (PPP)</string>
</resources>

M src/main/res/xml/settings.xml => src/main/res/xml/settings.xml +8 -10
@@ 1,22 1,20 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">

    <ListPreference
        android:key="tile_provider"
        android:title="@string/pref_tile_provider"
        android:defaultValue="OPEN_STREET_MAP"
        android:summary="%s"
        android:entries="@array/pref_tile_provider_titles"
        android:entryValues="@array/pref_tile_provider_values"
        android:key="tile_provider"
        android:negativeButtonText="@null"
        android:positiveButtonText="@null" />
        android:positiveButtonText="@null"
        android:summary="%s"
        android:title="@string/pref_tile_provider" />
    <CheckBoxPreference
        android:key="scale_tiles_for_high_dpi"
        android:title="@string/pref_scale_tiles"
        android:defaultValue="false"
        android:checked="false"
        android:defaultValue="false"
        android:key="scale_tiles_for_high_dpi"
        android:summary="@string/pref_scale_tiles_summary"
        />
    <com.samwhited.opensharelocationplugin.preferences.AboutPreference
        android:title="@string/title_activity_about"/>
        android:title="@string/pref_scale_tiles" />
    <com.samwhited.opensharelocationplugin.preferences.AboutPreference android:title="@string/title_activity_about" />

</PreferenceScreen>