Skip to content
This repository has been archived by the owner on Aug 23, 2023. It is now read-only.

Adds Fine permission UI to background permission sample. #223

Merged
merged 4 commits into from
Feb 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions LocationUpdatesBackgroundKotlin/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

apply plugin: 'kotlin-kapt'

android {
compileSdkVersion 29
buildToolsVersion "29.0.2"

defaultConfig {
applicationId "com.google.android.gms.location.sample.locationupdatesbackgroundkotlin"
minSdkVersion 15
Expand All @@ -30,12 +33,17 @@ android {
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

dataBinding {
enabled true
}
}

dependencies {
Expand All @@ -44,7 +52,12 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'

kapt 'com.android.databinding:compiler:3.1.4'

testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'com.google.android.material:material:1.1.0'
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,19 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.google.android.gms.location.sample.locationupdatesbackgroundkotlin">

<uses-permission android:name="android.permission.ACCESS_FINE_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">
android:theme="@style/AppTheme"
tools:ignore="AllowBackup,GoogleAppIndexingWarning">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,55 @@ package com.google.android.gms.location.sample.locationupdatesbackgroundkotlin

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.databinding.DataBindingUtil.setContentView

class MainActivity : AppCompatActivity() {


import com.google.android.gms.location.sample.locationupdatesbackgroundkotlin.PermissionRequestFragment.Callbacks
import com.google.android.gms.location.sample.locationupdatesbackgroundkotlin.databinding.ActivityMainBinding


/**
* This app allows a user to track their location in the background.
*
* IMPORTANT NOTE: You should always prefer 'while-in-use' location tracking, i.e., track location
* while the app is in use and create a foreground service (tied to a Notification) when the
* user navigates away from the app.
*
* If you do have an approved use case for tracking location in the background, it will require an
* additional permission.
*
* Note: Users have four options in Android 11+ regarding location:
*
* * One time only
* * Allow while app is in use, i.e., while app is in foreground
* * Allow all the time
* * Not allow location at all
*
* Best practice requires you spread out your first fine/course request and your background request.
*/
class MainActivity : AppCompatActivity(), Callbacks {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

setContentView<ActivityMainBinding>(this, R.layout.activity_main)

val currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container)

if (currentFragment == null) {

val fragment = PermissionRequestFragment.newInstance(PermissionRequestType.FINE_LOCATION)

supportFragmentManager
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.addToBackStack(null)
.commit()
}
}

override fun displayLocationUI() {
TODO("Add redirect to main app fragment")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
/*
* Copyright (C) 2020 Google Inc. All Rights Reserved.
*
* 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.google.android.gms.location.sample.locationupdatesbackgroundkotlin

import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat.checkSelfPermission
import androidx.fragment.app.Fragment

import com.google.android.gms.location.sample.locationupdatesbackgroundkotlin.databinding.FragmentPermissionRequestBinding
import com.google.android.material.snackbar.Snackbar

private const val TAG = "PermissionRequestFrag"

/**
* Displays information about why a user should enable either the FINE location permission or the
* background location permission (depending on what is needed).
*
* Allows users to grant the permissions as well.
*/
class PermissionRequestFragment : Fragment() {

// Set by Activity for which type of permission to request (Fine or background).
private var permissionRequestType: PermissionRequestType? = null

private lateinit var binding: FragmentPermissionRequestBinding

private var activityListener: Callbacks? = null


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

permissionRequestType =
arguments?.getSerializable(ARG_PERMISSION_REQUEST_TYPE) as PermissionRequestType
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {

binding = FragmentPermissionRequestBinding.inflate(inflater, container, false)

when (permissionRequestType) {
PermissionRequestType.FINE_LOCATION ->
binding.explanationTextView.text =
getString(R.string.fine_location_access_rationale_text)

PermissionRequestType.BACKGROUND_LOCATION ->
binding.explanationTextView.text =
getString(R.string.background_location_access_rationale_text)
}

binding.permissionRequestButton.setOnClickListener {
when (permissionRequestType) {
PermissionRequestType.FINE_LOCATION ->
requestFineLocationPermission()

PermissionRequestType.BACKGROUND_LOCATION ->
TODO("Add system background permission request.")
}
}

return binding.root
}

override fun onAttach(context: Context) {
super.onAttach(context)

if (context is Callbacks) {
activityListener = context
} else {
throw RuntimeException("$context must implement OnFragmentInteractionListener")
}
}

override fun onDetach() {
super.onDetach()

activityListener = null
}

override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
Log.d(TAG, "onRequestPermissionResult")

when (requestCode) {
REQUEST_FINE_LOCATION_PERMISSIONS_REQUEST_CODE -> when {
grantResults.isEmpty() ->
// If user interaction was interrupted, the permission request
// is cancelled and you receive an empty array.
Log.d(TAG, "User interaction was cancelled.")

grantResults[0] == PackageManager.PERMISSION_GRANTED ->
activityListener?.displayLocationUI()

else -> {

Snackbar.make(
binding.frameLayout,
R.string.permission_denied_explanation,
Snackbar.LENGTH_LONG
)
.setAction(R.string.settings) {
// Build intent that displays the App settings screen.
val intent = Intent()
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
val uri = Uri.fromParts(
"package",
BuildConfig.APPLICATION_ID,
null
)
intent.data = uri
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
}
.show()
}
}
}
}

private fun fineLocationPermissionApproved(): Boolean {

val context = context ?: return false

return PackageManager.PERMISSION_GRANTED == checkSelfPermission(
context,
Manifest.permission.ACCESS_FINE_LOCATION
)
}

private fun requestFineLocationPermission() {

if (fineLocationPermissionApproved()) {
activityListener?.displayLocationUI()

} else {

val provideRationale = shouldShowRequestPermissionRationale(
Manifest.permission.ACCESS_FINE_LOCATION
)

// If the user denied a previous request, but didn't check "Don't ask again", provide
// additional rationale.
if (provideRationale) {
Snackbar.make(
binding.frameLayout,
R.string.simple_permission_rationale,
Snackbar.LENGTH_LONG
)
.setAction(R.string.ok) {
requestPermissions(
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
REQUEST_FINE_LOCATION_PERMISSIONS_REQUEST_CODE
)
}
.show()
} else {
Log.d(TAG, "Request fine location permission")
requestPermissions(
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
REQUEST_FINE_LOCATION_PERMISSIONS_REQUEST_CODE
)
}
}
}


/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
*
*
* See the Android Training lesson [Communicating with Other Fragments]
* (http://developer.android.com/training/basics/fragments/communicating.html)
* for more information.
*/
interface Callbacks {
fun displayLocationUI()
}

companion object {
private const val ARG_PERMISSION_REQUEST_TYPE =
"com.google.android.gms.location.sample.locationupdatesbackgroundkotlin.PERMISSION_REQUEST_TYPE"

private const val REQUEST_FINE_LOCATION_PERMISSIONS_REQUEST_CODE = 34

/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param permissionRequestType Type of permission you would like to request.
* @return A new instance of fragment PermissionRequestFragment.
*/
@JvmStatic
fun newInstance(permissionRequestType: PermissionRequestType) =
PermissionRequestFragment().apply {
arguments = Bundle().apply {
putSerializable(ARG_PERMISSION_REQUEST_TYPE, permissionRequestType)
}
}
}
}

enum class PermissionRequestType {
FINE_LOCATION, BACKGROUND_LOCATION
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,16 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

</androidx.constraintlayout.widget.ConstraintLayout>
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
</FrameLayout>

</layout>
Loading