Update google fit integration

This commit is contained in:
Tommaso Berlose 2020-05-11 00:00:31 +02:00
parent 8f0f6bc868
commit ce9f5a6e45
10 changed files with 193 additions and 250 deletions

Binary file not shown.

View File

@ -22,7 +22,7 @@ android {
applicationId "com.tommasoberlose.anotherwidget"
minSdkVersion 23
targetSdkVersion 29
versionCode 85
versionCode 86
versionName "2.0.8"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

Binary file not shown.

View File

@ -86,44 +86,16 @@
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="com.tommasoberlose.anotherwidget.action.ACTION_WEATHER_UPDATE" />
<category android:name="android.intent.category.DEFAULT" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.TIME_SET" />
<action android:name="android.intent.action.TIMEZONE_CHANGED" />
<action android:name="android.intent.action.LOCALE_CHANGED" />
</intent-filter>
</receiver>
<receiver
android:name=".receivers.PlayerReceiver"
android:enabled="true"
android:exported="true"
tools:ignore="ExportedReceiver">
<intent-filter>
<action android:name="com.android.music.metachanged" />
<action android:name="com.android.music.playstatechanged" />
<action android:name="com.android.music.playbackcomplete" />
<action android:name="com.android.music.queuechanged" />
<action android:name="com.htc.music.metachanged" />
<action android:name="fm.last.android.metachanged" />
<action android:name="com.sec.android.app.music.metachanged" />
<action android:name="com.nullsoft.winamp.metachanged" />
<action android:name="com.amazon.mp3.metachanged" />
<action android:name="com.miui.player.metachanged" />
<action android:name="com.real.IMP.metachanged" />
<action android:name="com.sonyericsson.music.metachanged" />
<action android:name="com.rdio.android.metachanged" />
<action android:name="com.samsung.sec.android.MusicPlayer.metachanged" />
<action android:name="com.andrew.apollo.metachanged" />
<action android:name="com.spotify.music.playbackstatechanged"/>
<action android:name="com.spotify.music.metadatachanged"/>
<action android:name="com.spotify.music.queuechanged"/>
</intent-filter>
</receiver>
<receiver
android:name=".receivers.WidgetClickListenerReceiver"
android:enabled="true"
@ -162,11 +134,13 @@
</intent-filter>
</receiver>
<receiver android:name=".receivers.FenceReceiver"
<receiver android:name=".receivers.ActivityDetectionReceiver"
android:exported="false"
android:permission="com.google.android.gms.permission.ACTIVITY_RECOGNITION">
<intent-filter>
<action android:name="com.mypackage.ACTION_PROCESS_ACTIVITY_TRANSITIONS" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>

View File

@ -1,97 +0,0 @@
package com.tommasoberlose.anotherwidget.helpers
import android.Manifest
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import com.google.android.gms.location.ActivityRecognition
import com.google.android.gms.location.ActivityTransition
import com.google.android.gms.location.ActivityTransitionRequest
import com.google.android.gms.location.DetectedActivity
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.receivers.FenceReceiver
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
object DailyStepsHelper {
fun registerFence(context: Context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || context.checkGrantedPermission(Manifest.permission.ACTIVITY_RECOGNITION)) {
val transitions = mutableListOf<ActivityTransition>()
transitions +=
ActivityTransition.Builder()
.setActivityType(DetectedActivity.WALKING)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
.build()
transitions +=
ActivityTransition.Builder()
.setActivityType(DetectedActivity.WALKING)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
.build()
transitions +=
ActivityTransition.Builder()
.setActivityType(DetectedActivity.RUNNING)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
.build()
transitions +=
ActivityTransition.Builder()
.setActivityType(DetectedActivity.RUNNING)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
.build()
transitions +=
ActivityTransition.Builder()
.setActivityType(DetectedActivity.STILL)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
.build()
val request = ActivityTransitionRequest(transitions)
// myPendingIntent is the instance of PendingIntent where the app receives callbacks.
val task = ActivityRecognition.getClient(context)
.requestActivityTransitionUpdates(
request,
PendingIntent.getBroadcast(
context,
2,
Intent(context, FenceReceiver::class.java),
0
)
)
task.addOnFailureListener { e: Exception ->
e.printStackTrace()
Preferences.showDailySteps = false
}
}
}
fun unregisterFence(context: Context) {
val task = ActivityRecognition.getClient(context)
.removeActivityTransitionUpdates(
PendingIntent.getBroadcast(
context,
2,
Intent(context, FenceReceiver::class.java),
0
)
)
task.addOnCompleteListener {
if (it.isSuccessful) {
PendingIntent.getBroadcast(
context,
2,
Intent(context, FenceReceiver::class.java),
0
).cancel()
}
}
}
}

View File

@ -0,0 +1,171 @@
package com.tommasoberlose.anotherwidget.receivers
import android.Manifest
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import android.util.Log
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.fitness.Fitness
import com.google.android.gms.fitness.FitnessOptions
import com.google.android.gms.fitness.data.DataType
import com.google.android.gms.fitness.data.Field.FIELD_STEPS
import com.google.android.gms.fitness.request.DataReadRequest
import com.google.android.gms.location.*
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import java.util.*
import java.util.concurrent.TimeUnit
class ActivityDetectionReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (ActivityTransitionResult.hasResult(intent)) {
val result = ActivityTransitionResult.extractResult(intent)!!
val lastEvent = result.transitionEvents.last()
if (lastEvent.activityType == DetectedActivity.WALKING || lastEvent.activityType == DetectedActivity.RUNNING && lastEvent.transitionType == ActivityTransition.ACTIVITY_TRANSITION_EXIT) {
requestDailySteps(context)
setTimeout(context)
} else {
resetDailySteps()
}
} else {
if (intent.action == Intent.ACTION_BOOT_COMPLETED || intent.action == Intent.ACTION_MY_PACKAGE_REPLACED && Preferences.showDailySteps && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || context.checkGrantedPermission(Manifest.permission.ACTIVITY_RECOGNITION)) {
registerFence(context)
} else {
resetDailySteps()
}
}
}
private fun resetDailySteps() {
Preferences.googleFitSteps = -1
}
companion object {
val FITNESS_OPTIONS: FitnessOptions = FitnessOptions.builder()
.addDataType(DataType.TYPE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ)
.addDataType(DataType.AGGREGATE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ)
.build()
fun registerFence(context: Context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || context.checkGrantedPermission(
Manifest.permission.ACTIVITY_RECOGNITION)) {
val transitions = mutableListOf<ActivityTransition>()
transitions +=
ActivityTransition.Builder()
.setActivityType(DetectedActivity.WALKING)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
.build()
transitions +=
ActivityTransition.Builder()
.setActivityType(DetectedActivity.RUNNING)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
.build()
val request = ActivityTransitionRequest(transitions)
// myPendingIntent is the instance of PendingIntent where the app receives callbacks.
val task = ActivityRecognition.getClient(context)
.requestActivityTransitionUpdates(
request,
PendingIntent.getBroadcast(
context,
2,
Intent(context, ActivityDetectionReceiver::class.java),
0
)
)
task.addOnFailureListener { e: Exception ->
e.printStackTrace()
Preferences.showDailySteps = false
}
}
}
fun unregisterFence(context: Context) {
val task = ActivityRecognition.getClient(context)
.removeActivityTransitionUpdates(
PendingIntent.getBroadcast(
context,
2,
Intent(context, ActivityDetectionReceiver::class.java),
0
)
)
task.addOnCompleteListener {
if (it.isSuccessful) {
PendingIntent.getBroadcast(
context,
2,
Intent(context, ActivityDetectionReceiver::class.java),
0
).cancel()
}
}
}
fun requestDailySteps(context: Context) {
val account: GoogleSignInAccount? = GoogleSignIn.getLastSignedInAccount(context)
if (account != null && GoogleSignIn.hasPermissions(account, FITNESS_OPTIONS)) {
val cal: Calendar = Calendar.getInstance()
cal.set(Calendar.HOUR_OF_DAY, 0)
cal.set(Calendar.MINUTE, 0)
cal.set(Calendar.SECOND, 0)
cal.set(Calendar.MILLISECOND, 0)
val startTime: Long = cal.timeInMillis
cal.add(Calendar.DAY_OF_YEAR, 1)
val endTime: Long = cal.timeInMillis
val readRequest = DataReadRequest.Builder()
.aggregate(
DataType.TYPE_STEP_COUNT_DELTA,
DataType.AGGREGATE_STEP_COUNT_DELTA
)
.setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
.bucketByTime(1, TimeUnit.DAYS)
.build()
Fitness.getHistoryClient(context, account)
.readData(readRequest)
.addOnSuccessListener { response ->
Preferences.googleFitSteps = response.buckets.sumBy {
it.getDataSet(DataType.AGGREGATE_STEP_COUNT_DELTA)?.dataPoints?.get(0)?.getValue(FIELD_STEPS)?.asInt() ?: 0
}.toLong()
MainWidget.updateWidget(context)
}
}
}
fun setTimeout(context: Context) {
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
cancel(PendingIntent.getBroadcast(context, 0, Intent(context, ActivityDetectionReceiver::class.java), 0))
setExactAndAllowWhileIdle(
AlarmManager.RTC,
Calendar.getInstance().timeInMillis + 15 * 60 * 1000,
PendingIntent.getBroadcast(
context,
0,
Intent(context, ActivityDetectionReceiver::class.java),
0
)
)
}
}
}
}

View File

@ -1,84 +0,0 @@
package com.tommasoberlose.anotherwidget.receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.fitness.Fitness
import com.google.android.gms.fitness.FitnessOptions
import com.google.android.gms.fitness.data.DataSource
import com.google.android.gms.fitness.data.DataType
import com.google.android.gms.fitness.data.Field
import com.google.android.gms.fitness.data.Field.FIELD_STEPS
import com.google.android.gms.fitness.request.DataReadRequest
import com.google.android.gms.location.ActivityTransitionResult
import com.google.android.gms.location.DetectedActivity
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import java.util.*
import java.util.concurrent.TimeUnit
class FenceReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (ActivityTransitionResult.hasResult(intent)) {
val result = ActivityTransitionResult.extractResult(intent)!!
val lastEvent = result.transitionEvents.last()
if (lastEvent.activityType == DetectedActivity.WALKING || lastEvent.activityType == DetectedActivity.RUNNING) {
requestDailySteps(context)
} else {
resetDailySteps()
}
}
}
private fun resetDailySteps() {
Preferences.googleFitSteps = -1
}
companion object {
val FITNESS_OPTIONS = FitnessOptions.builder()
.addDataType(DataType.TYPE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ)
.addDataType(DataType.AGGREGATE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ)
.build()
fun requestDailySteps(context: Context) {
val account: GoogleSignInAccount? = GoogleSignIn.getLastSignedInAccount(context)
if (account != null && GoogleSignIn.hasPermissions(account, FITNESS_OPTIONS)) {
val cal: Calendar = Calendar.getInstance()
cal.set(Calendar.HOUR_OF_DAY, 0)
cal.set(Calendar.MINUTE, 0)
cal.set(Calendar.SECOND, 0)
cal.set(Calendar.MILLISECOND, 0)
val startTime: Long = cal.timeInMillis
cal.add(Calendar.DAY_OF_YEAR, 1)
val endTime: Long = cal.timeInMillis
val readRequest = DataReadRequest.Builder()
.aggregate(
DataType.TYPE_STEP_COUNT_DELTA,
DataType.AGGREGATE_STEP_COUNT_DELTA
)
.setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
.bucketByTime(1, TimeUnit.DAYS)
.build()
Fitness.getHistoryClient(context, account)
.readData(readRequest)
.addOnSuccessListener { response ->
Preferences.googleFitSteps = response.buckets.sumBy {
it.getDataSet(DataType.AGGREGATE_STEP_COUNT_DELTA)?.dataPoints?.get(0)?.getValue(FIELD_STEPS)?.asInt() ?: 0
}.toLong()
MainWidget.updateWidget(context)
}
}
}
}
}

View File

@ -1,22 +0,0 @@
package com.tommasoberlose.anotherwidget.receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
class PlayerReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("ciao", "player ok")
// val cmd = intent.getStringExtra("command")
// Log.v("tag ", "$action / $cmd")
// val artist = intent.getStringExtra("artist")
// val album = intent.getStringExtra("album")
// val track = intent.getStringExtra("track")
// Log.v("tag", "$artist:$album:$track")
}
}

View File

@ -176,7 +176,7 @@ class GeneralTabFragment : Fragment() {
private fun setupListener() {
action_main_text_size.setOnClickListener {
val dialog = BottomSheetMenu<Float>(requireContext(), header = getString(R.string.title_main_text_size)).setSelectedValue(Preferences.textMainSize)
(32 downTo 10).filter { it % 2 == 0 }.forEach {
(40 downTo 10).filter { it % 2 == 0 }.forEach {
dialog.addItem("${it}sp", it.toFloat())
}
dialog.addOnSelectItemListener { value ->
@ -186,7 +186,7 @@ class GeneralTabFragment : Fragment() {
action_second_text_size.setOnClickListener {
val dialog = BottomSheetMenu<Float>(requireContext(), header = getString(R.string.title_second_text_size)).setSelectedValue(Preferences.textSecondSize)
(28 downTo 10).filter { it % 2 == 0 }.forEach {
(40 downTo 10).filter { it % 2 == 0 }.forEach {
dialog.addItem("${it}sp", it.toFloat())
}
dialog.addOnSelectItemListener { value ->

View File

@ -25,9 +25,6 @@ import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.fitness.FitnessOptions
import com.google.android.gms.fitness.data.DataType
import com.google.android.gms.tasks.Task
import com.karumi.dexter.Dexter
import com.karumi.dexter.MultiplePermissionsReport
import com.karumi.dexter.PermissionToken
@ -40,10 +37,9 @@ import com.tommasoberlose.anotherwidget.components.GlanceProviderSortMenu
import com.tommasoberlose.anotherwidget.databinding.FragmentGlanceSettingsBinding
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.AlarmHelper
import com.tommasoberlose.anotherwidget.helpers.DailyStepsHelper
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
import com.tommasoberlose.anotherwidget.receivers.FenceReceiver
import com.tommasoberlose.anotherwidget.receivers.FenceReceiver.Companion.FITNESS_OPTIONS
import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver
import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver.Companion.FITNESS_OPTIONS
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
@ -198,8 +194,13 @@ class GlanceTabFragment : Fragment() {
.addItem(getString(R.string.settings_not_visible), false)
.addOnSelectItemListener { value ->
if (value) {
val mGoogleSignInClient = GoogleSignIn.getClient(requireActivity(), GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN).addExtension(FITNESS_OPTIONS).build())
startActivityForResult(mGoogleSignInClient.signInIntent, 2)
val account: GoogleSignInAccount? = GoogleSignIn.getLastSignedInAccount(requireContext())
if (!GoogleSignIn.hasPermissions(account, FITNESS_OPTIONS)) {
val mGoogleSignInClient = GoogleSignIn.getClient(requireActivity(), GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN).addExtension(FITNESS_OPTIONS).build())
startActivityForResult(mGoogleSignInClient.signInIntent, 2)
} else {
Preferences.showDailySteps = true
}
} else {
Preferences.showDailySteps = false
}
@ -270,20 +271,20 @@ class GlanceTabFragment : Fragment() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || activity?.checkGrantedPermission(Manifest.permission.ACTIVITY_RECOGNITION) == true) {
fitness_permission_alert?.isVisible = false
if (Preferences.showDailySteps) {
DailyStepsHelper.registerFence(requireContext())
ActivityDetectionReceiver.registerFence(requireContext())
} else {
DailyStepsHelper.unregisterFence(requireContext())
ActivityDetectionReceiver.unregisterFence(requireContext())
}
show_steps_label?.text = if (Preferences.showDailySteps) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
} else if (Preferences.showDailySteps) {
DailyStepsHelper.unregisterFence(requireContext())
ActivityDetectionReceiver.unregisterFence(requireContext())
fitness_permission_alert?.isVisible = true
show_steps_label?.text = getString(R.string.settings_request_fitness_access)
fitness_permission_alert?.setOnClickListener {
requireFitnessPermission()
}
} else {
DailyStepsHelper.unregisterFence(requireContext())
ActivityDetectionReceiver.unregisterFence(requireContext())
show_steps_label?.text = getString(R.string.settings_not_visible)
fitness_permission_alert?.isVisible = false
}
@ -297,7 +298,7 @@ class GlanceTabFragment : Fragment() {
when (requestCode) {
1 -> {
if (resultCode == Activity.RESULT_OK) {
DailyStepsHelper.registerFence(requireContext())
checkFitnessPermission()
} else {
Preferences.showDailySteps = false
}
@ -312,7 +313,7 @@ class GlanceTabFragment : Fragment() {
account,
FITNESS_OPTIONS)
} else {
DailyStepsHelper.registerFence(requireContext())
checkFitnessPermission()
}
} catch (e: ApiException) {
e.printStackTrace()