diff options
author | Anil Admal <aadmal@google.com> | 2019-04-10 17:05:30 -0700 |
---|---|---|
committer | Anil Admal <aadmal@google.com> | 2019-04-16 15:49:26 -0700 |
commit | 54ae24843ca798bdc62ac951a64b034775ab8208 (patch) | |
tree | f58aa597d83be4cd0f94d274576da7cc001e24e6 /src/com/example | |
parent | ddf38e938b92e7c10f5b75fea9be51bec901332e (diff) | |
download | SampleLocationAttribution-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')
-rw-r--r-- | src/com/example/android/locationattribution/MainActivity.java | 191 |
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); + } + } +} |