summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnil Admal <aadmal@google.com>2019-04-10 17:05:30 -0700
committerAnil Admal <aadmal@google.com>2019-04-16 15:49:26 -0700
commit54ae24843ca798bdc62ac951a64b034775ab8208 (patch)
treef58aa597d83be4cd0f94d274576da7cc001e24e6
parentddf38e938b92e7c10f5b75fea9be51bec901332e (diff)
downloadSampleLocationAttribution-54ae24843ca798bdc62ac951a64b034775ab8208.tar.gz
Add AOSP sample non-framework Location Attribution APK
Bug: 123375197 Test: Verified the sample app on a Pixel device. Change-Id: Ie94c68f6c4d389e736b810afa884411f5dd0b6e8
-rw-r--r--Android.mk25
-rw-r--r--AndroidManifest.xml46
-rw-r--r--OWNERS4
-rw-r--r--res/drawable-v24/ic_launcher_foreground.xml34
-rw-r--r--res/drawable/button_modify_location_settings_shape.xml6
-rw-r--r--res/drawable/ic_launcher_background.xml170
-rw-r--r--res/layout/activity_main.xml42
-rw-r--r--res/mipmap-anydpi-v26/ic_launcher.xml5
-rw-r--r--res/mipmap-anydpi-v26/ic_launcher_round.xml5
-rw-r--r--res/mipmap-hdpi/ic_launcher.pngbin0 -> 2963 bytes
-rw-r--r--res/mipmap-hdpi/ic_launcher_round.pngbin0 -> 4905 bytes
-rw-r--r--res/mipmap-mdpi/ic_launcher.pngbin0 -> 2060 bytes
-rw-r--r--res/mipmap-mdpi/ic_launcher_round.pngbin0 -> 2783 bytes
-rw-r--r--res/mipmap-xhdpi/ic_launcher.pngbin0 -> 4490 bytes
-rw-r--r--res/mipmap-xhdpi/ic_launcher_round.pngbin0 -> 6895 bytes
-rw-r--r--res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 6387 bytes
-rw-r--r--res/mipmap-xxhdpi/ic_launcher_round.pngbin0 -> 10413 bytes
-rw-r--r--res/mipmap-xxxhdpi/ic_launcher.pngbin0 -> 9128 bytes
-rw-r--r--res/mipmap-xxxhdpi/ic_launcher_round.pngbin0 -> 15132 bytes
-rw-r--r--res/values/colors.xml8
-rw-r--r--res/values/strings.xml19
-rw-r--r--res/values/styles.xml11
-rw-r--r--src/com/example/android/locationattribution/MainActivity.java191
23 files changed, 566 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..d2e2a8d
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,25 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_USE_AAPT2 := true
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+ androidx.core_core \
+ androidx.appcompat_appcompat
+
+# Build all java files in the src subdirectory
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PRIVILEGED_MODULE := true
+LOCAL_PRODUCT_MODULE := true
+
+# Name of the APK to build
+LOCAL_PACKAGE_NAME := SampleLocationAttribution
+
+# Tell it to build an APK
+include $(BUILD_PACKAGE)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644
index 0000000..04029ca
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.locationattribution">
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <!-- Requires permission "Allow all the time" and not just "Allow only while the app is in use" -->
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme">
+ <activity android:name=".MainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.APPLICATION_PREFERENCES" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="com.example.android.locationattribution.intent.action.NON_FRAMEWORK_LOCATION" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..0a92ab5
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,4 @@
+aadmal@google.com
+gomo@google.com
+wyattriley@google.com
+yuhany@google.com
diff --git a/res/drawable-v24/ic_launcher_foreground.xml b/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..1f6bb29
--- /dev/null
+++ b/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,34 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path
+ android:fillType="evenOdd"
+ android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
+ android:strokeWidth="1"
+ android:strokeColor="#00000000">
+ <aapt:attr name="android:fillColor">
+ <gradient
+ android:endX="78.5885"
+ android:endY="90.9159"
+ android:startX="48.7653"
+ android:startY="61.0927"
+ android:type="linear">
+ <item
+ android:color="#44000000"
+ android:offset="0.0" />
+ <item
+ android:color="#00000000"
+ android:offset="1.0" />
+ </gradient>
+ </aapt:attr>
+ </path>
+ <path
+ android:fillColor="#FFFFFF"
+ android:fillType="nonZero"
+ android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
+ android:strokeWidth="1"
+ android:strokeColor="#00000000" />
+</vector>
diff --git a/res/drawable/button_modify_location_settings_shape.xml b/res/drawable/button_modify_location_settings_shape.xml
new file mode 100644
index 0000000..e321c5d
--- /dev/null
+++ b/res/drawable/button_modify_location_settings_shape.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@color/google_blue600" />
+ <corners android:radius="7dp" />
+</shape> \ No newline at end of file
diff --git a/res/drawable/ic_launcher_background.xml b/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..0d025f9
--- /dev/null
+++ b/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path
+ android:fillColor="#008577"
+ android:pathData="M0,0h108v108h-108z" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M9,0L9,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,0L19,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M29,0L29,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M39,0L39,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M49,0L49,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M59,0L59,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M69,0L69,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M79,0L79,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M89,0L89,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M99,0L99,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,9L108,9"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,19L108,19"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,29L108,29"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,39L108,39"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,49L108,49"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,59L108,59"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,69L108,69"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,79L108,79"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,89L108,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,99L108,99"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,29L89,29"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,39L89,39"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,49L89,49"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,59L89,59"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,69L89,69"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,79L89,79"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M29,19L29,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M39,19L39,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M49,19L49,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M59,19L59,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M69,19L69,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M79,19L79,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+</vector>
diff --git a/res/layout/activity_main.xml b/res/layout/activity_main.xml
new file mode 100644
index 0000000..55f3d12
--- /dev/null
+++ b/res/layout/activity_main.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:scrollbarStyle="outsideOverlay" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal" >
+
+ <TextView
+ android:id="@+id/textViewAppInfo"
+ android:layout_width="321dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginTop="24dp"
+ android:layout_gravity="center"
+ android:textColorLink="@color/google_blue600"
+ android:text="@string/textViewAppInfo"
+ android:textSize="16sp"
+ android:textAppearance="@android:style/TextAppearance" />
+
+ <Button
+ android:id="@+id/buttonModifyLocationSettings"
+ style="@android:style/Widget.DeviceDefault.Button.Borderless"
+ android:layout_width="264dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="48dp"
+ android:gravity="center_vertical|center_horizontal"
+ android:background="@drawable/button_modify_location_settings_shape"
+ android:text="@string/buttonModifyLocationSettings"
+ android:textAllCaps="false"
+ android:textColor="@android:color/white"/>
+ </LinearLayout>
+</ScrollView> \ No newline at end of file
diff --git a/res/mipmap-anydpi-v26/ic_launcher.xml b/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background" />
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon> \ No newline at end of file
diff --git a/res/mipmap-anydpi-v26/ic_launcher_round.xml b/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background" />
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon> \ No newline at end of file
diff --git a/res/mipmap-hdpi/ic_launcher.png b/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..898f3ed
--- /dev/null
+++ b/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/res/mipmap-hdpi/ic_launcher_round.png b/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..dffca36
--- /dev/null
+++ b/res/mipmap-hdpi/ic_launcher_round.png
Binary files differ
diff --git a/res/mipmap-mdpi/ic_launcher.png b/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..64ba76f
--- /dev/null
+++ b/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/res/mipmap-mdpi/ic_launcher_round.png b/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..dae5e08
--- /dev/null
+++ b/res/mipmap-mdpi/ic_launcher_round.png
Binary files differ
diff --git a/res/mipmap-xhdpi/ic_launcher.png b/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..e5ed465
--- /dev/null
+++ b/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/res/mipmap-xhdpi/ic_launcher_round.png b/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..14ed0af
--- /dev/null
+++ b/res/mipmap-xhdpi/ic_launcher_round.png
Binary files differ
diff --git a/res/mipmap-xxhdpi/ic_launcher.png b/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..b0907ca
--- /dev/null
+++ b/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/res/mipmap-xxhdpi/ic_launcher_round.png b/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..d8ae031
--- /dev/null
+++ b/res/mipmap-xxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/res/mipmap-xxxhdpi/ic_launcher.png b/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..2c18de9
--- /dev/null
+++ b/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/res/mipmap-xxxhdpi/ic_launcher_round.png b/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..beed3cd
--- /dev/null
+++ b/res/mipmap-xxxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/res/values/colors.xml b/res/values/colors.xml
new file mode 100644
index 0000000..d0b29e5
--- /dev/null
+++ b/res/values/colors.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="colorPrimary">#008577</color>
+ <color name="colorPrimaryDark">#00574B</color>
+ <color name="colorAccent">#D81B60</color>
+ <color name="colorBlack">#000000</color>
+ <color name="google_blue600">#1a73e8</color>
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644
index 0000000..f4b3404
--- /dev/null
+++ b/res/values/strings.xml
@@ -0,0 +1,19 @@
+<resources>
+ <string name="app_name">Sample Location Attribution</string>
+ <string name="textViewAppInfo"></string>
+ <!--
+ - The main screen (and the only one for this app) displays the text to the user regarding
+ - device's location access by Non-Android entities outside the Android framework control.
+ - Each string-array item denotes a separate paragraph.
+ -->
+ <string-array name="textViewAppInfo_Paragraphs">
+ <item>REPLACE THE TEXT BELOW WITH YOUR OWN TEXT EXPLAINING YOUR NON-ANDROID LOCATION ACCESS USE CASE(S).\n\n</item>
+ <item>This service ...\n</item>
+ <item>&lt;explain what your service does here ... &gt;\n\n</item>
+ <item>To allow this service to access your device GPS location, enable location permission at the "Allow all the time" level.\n\n</item>
+ <item><a href="location_attribution_app://learn_more">Learn more</a>\n</item>
+ </string-array>
+ <!-- NOTE: Replace this link with the actual link to your Non-Android service's help web page -->
+ <string name="urlLearnMore" translatable="false">https://support.oem-name-here.com/path/doc</string>
+ <string name="buttonModifyLocationSettings">Modify app location permission</string>
+</resources> \ No newline at end of file
diff --git a/res/values/styles.xml b/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/res/values/styles.xml
@@ -0,0 +1,11 @@
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ <item name="colorPrimary">@color/colorPrimary</item>
+ <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+ <item name="colorAccent">@color/colorAccent</item>
+ </style>
+
+</resources>
diff --git a/src/com/example/android/locationattribution/MainActivity.java b/src/com/example/android/locationattribution/MainActivity.java
new file mode 100644
index 0000000..1ca5c35
--- /dev/null
+++ b/src/com/example/android/locationattribution/MainActivity.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.locationattribution;
+
+import android.Manifest;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import androidx.appcompat.app.AppCompatActivity;
+import android.os.Bundle;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.TextPaint;
+import android.text.method.LinkMovementMethod;
+import android.text.style.ClickableSpan;
+import android.text.style.URLSpan;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+/**
+ * Non-framework Location Attribution sample application.
+ *
+ * <p>This location attribution sample application demonstrates how to give user visibility
+ * and control of non-user-emergency location access by non-framework entities accessing GNSS
+ * chipset API directly bypassing the standard Android framework location permission settings.
+ *
+ * <p>Displays text to the user about the benefits of giving location permission to this app so
+ * that the non-framework entity or entities this app represents can access device location from
+ * GNSS chipset directly.
+ *
+ * <p>Provides a button to allow the user to modify the location permission settings for this app.
+ */
+public class MainActivity extends AppCompatActivity {
+ private static final String APPLICATION_ID = "com.example.android.locationattribution";
+ private static final String TAG = "LocationAttribution";
+ private static final String PREFS_FILE_NAME = "LocationAttributionPrefs";
+ private static final int NON_FRAMEWORK_LOCATION_PERMISSION = 100;
+
+ private static final String URL_PREFIX = "location_attribution_app://";
+ private static final String LINK_LEARN_MORE = URL_PREFIX + "learn_more";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ // Set non-framework location access use case description to display.
+ setTextViewAppInfoContent();
+
+ // Show button for the user to modify location settings for this app.
+ Button button = (Button)findViewById(R.id.buttonModifyLocationSettings);
+ button.setOnClickListener(createModifyLocationSettingsButtonClickListener());
+ }
+
+ private void setTextViewAppInfoContent() {
+ // This text is seen by the user when this app is opened through the App info screen in
+ // Android Settings or when an intent is sent by carrier's own app.
+ TextView textViewAppInfo = findViewById(R.id.textViewAppInfo);
+ SpannableStringBuilder textViewAppInfoText = new SpannableStringBuilder();
+ for (CharSequence paragraph : getResources().getTextArray(
+ R.array.textViewAppInfo_Paragraphs)) {
+ textViewAppInfoText.append(paragraph);
+ }
+
+ replaceUrlSpansWithClickableSpans(textViewAppInfoText);
+ textViewAppInfo.setText(textViewAppInfoText);
+ textViewAppInfo.setMovementMethod(LinkMovementMethod.getInstance());
+ }
+
+ private View.OnClickListener createModifyLocationSettingsButtonClickListener() {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (ContextCompat.checkSelfPermission(MainActivity.this,
+ Manifest.permission.ACCESS_FINE_LOCATION)
+ == PackageManager.PERMISSION_GRANTED) {
+ // We can't show tri-state dialog when permission is already granted.
+ // So, go to the location permission settings screen directly.
+ showLocationPermissionSettingsDashboard();
+ return;
+ }
+
+ if (isFirstTimeAskingLocationPermission()) {
+ // Show tri-state dialog to change permission.
+ setFirstTimeAskingLocationPermission(false);
+ showRequestLocationPermissionDialog();
+ return;
+ }
+
+ if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
+ Manifest.permission.ACCESS_FINE_LOCATION)) {
+ // The user has previously denied the request. Show the tri-state dialog again.
+ showRequestLocationPermissionDialog();
+ } else {
+ // User has denied permission and selected 'Don't ask again' option.
+ showLocationPermissionSettingsDashboard();
+ }
+ }
+ };
+ }
+
+ private void showRequestLocationPermissionDialog() {
+ ActivityCompat.requestPermissions(this,
+ new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
+ NON_FRAMEWORK_LOCATION_PERMISSION);
+ }
+
+ private void showLocationPermissionSettingsDashboard() {
+ startActivity(new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
+ Uri.parse("package:" + APPLICATION_ID)));
+ }
+
+ private void setFirstTimeAskingLocationPermission(boolean isFirstTime) {
+ SharedPreferences sharedPreference = getApplicationContext().getSharedPreferences(
+ PREFS_FILE_NAME, MODE_PRIVATE);
+ SharedPreferences.Editor editor = sharedPreference.edit();
+ editor.putBoolean(Manifest.permission.ACCESS_FINE_LOCATION, isFirstTime).apply();
+ editor.commit();
+ }
+
+ private boolean isFirstTimeAskingLocationPermission() {
+ return getApplicationContext().getSharedPreferences(PREFS_FILE_NAME,
+ MODE_PRIVATE).getBoolean(Manifest.permission.ACCESS_FINE_LOCATION, true);
+ }
+
+ /**
+ * A clickable text listener.
+ *
+ * <p>Used to listen to click events for clickable text in the description displayed by this
+ * activity's main screen and navigate to the appropriate screen based on the text link
+ * clicked.
+ */
+ private class AppInfoTextLinkClickableSpan extends ClickableSpan {
+ private final String mUrl;
+
+ private AppInfoTextLinkClickableSpan(String url) {
+ mUrl = url;
+ }
+
+ @Override
+ public void onClick(View textView) {
+ switch (mUrl) {
+ case LINK_LEARN_MORE:
+ startActivity(new Intent(Intent.ACTION_VIEW,
+ Uri.parse(getString(R.string.urlLearnMore))));
+ break;
+ default:
+ Log.e(TAG, "@string/textViewAppInfo contains invalid URL: " + mUrl);
+ }
+ }
+
+ @Override
+ public void updateDrawState(TextPaint drawState) {
+ super.updateDrawState(drawState);
+ drawState.setUnderlineText(false);
+ }
+ }
+
+ /*
+ * The description text in {@code textAppInfo} shown in the activity screen has URL links.
+ * Replace those links with clickable links so that we get notified when those links are
+ * clicked. We can then navigate to different screens based on the links clicked.
+ */
+ private void replaceUrlSpansWithClickableSpans(Spannable textAppInfo) {
+ for(URLSpan span: textAppInfo.getSpans(0, textAppInfo.length(), URLSpan.class)) {
+ int start = textAppInfo.getSpanStart(span);
+ int end = textAppInfo.getSpanEnd(span);
+ textAppInfo.removeSpan(span);
+ textAppInfo.setSpan(new AppInfoTextLinkClickableSpan(span.getURL()), start, end, 0);
+ }
+ }
+}