summaryrefslogtreecommitdiff
path: root/src/com/example/android/locationattribution/MainActivity.java
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 /src/com/example/android/locationattribution/MainActivity.java
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
Diffstat (limited to 'src/com/example/android/locationattribution/MainActivity.java')
-rw-r--r--src/com/example/android/locationattribution/MainActivity.java191
1 files changed, 191 insertions, 0 deletions
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);
+ }
+ }
+}