| /* |
| * 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.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 (isLocationPermissionGranted(Manifest.permission.ACCESS_FINE_LOCATION)) { |
| if (!isLocationPermissionGranted( |
| Manifest.permission.ACCESS_BACKGROUND_LOCATION)) { |
| // Request 'Allow all the time' permission if the user didn't select |
| // 'Don't ask again' option earlier. |
| if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, |
| Manifest.permission.ACCESS_BACKGROUND_LOCATION)) { |
| showRequestBackgroundLocationPermissionDialog(); |
| return; |
| } |
| } |
| |
| // 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 boolean isLocationPermissionGranted(String locationPermissionType) { |
| return ActivityCompat.checkSelfPermission(this, locationPermissionType) |
| == PackageManager.PERMISSION_GRANTED; |
| } |
| |
| private void showRequestLocationPermissionDialog() { |
| ActivityCompat.requestPermissions(this, |
| new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, |
| NON_FRAMEWORK_LOCATION_PERMISSION); |
| } |
| |
| private void showRequestBackgroundLocationPermissionDialog() { |
| ActivityCompat.requestPermissions(this, |
| new String[]{Manifest.permission.ACCESS_BACKGROUND_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); |
| } |
| } |
| } |