Skip to content

Commit b1bf548

Browse files
Fix answering calls with app not in foreground
1 parent d16f0e9 commit b1bf548

File tree

10 files changed

+101
-35
lines changed

10 files changed

+101
-35
lines changed

app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ android {
1010
applicationId "me.hackerchick.raisetoanswer"
1111
minSdkVersion 26
1212
targetSdkVersion 29
13-
versionCode 2
14-
versionName "1.1"
13+
versionCode 3
14+
versionName "1.1.1"
1515

1616
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
1717
}

app/src/main/AndroidManifest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
66
<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />
7+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
78

89
<application
910
android:allowBackup="true"
@@ -24,6 +25,8 @@
2425
<action android:name="android.intent.action.PHONE_STATE" />
2526
</intent-filter>
2627
</receiver>
28+
29+
<service android:enabled="true" android:name=".RaiseToAnswerSensorEventListener" />
2730
</application>
2831

2932
</manifest>

app/src/main/java/me/hackerchick/raisetoanswer/MainActivity.kt

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,46 +2,48 @@ package me.hackerchick.raisetoanswer
22

33
import android.Manifest
44
import android.content.Context
5+
import android.content.Intent
56
import android.content.pm.PackageManager
67
import android.hardware.Sensor
78
import android.hardware.SensorManager
89
import android.os.Bundle
910
import android.widget.Button
10-
import android.widget.CompoundButton
1111
import android.widget.Switch
1212
import android.widget.Toast
1313
import androidx.appcompat.app.AppCompatActivity
1414
import androidx.core.app.ActivityCompat
15+
import java.util.*
16+
import java.util.concurrent.ScheduledThreadPoolExecutor
17+
import java.util.concurrent.TimeUnit
1518

1619

1720
class MainActivity : AppCompatActivity() {
1821
private var PERMISSION_REQUEST_READ_PHONE_STATE = 1
1922

20-
private var mSensorEventListener: RaiseToAnswerSensorEventListener? = null
23+
private val setListenerState: Stack<Boolean> = Stack()
2124

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

29+
val intent = Intent(this, RaiseToAnswerSensorEventListener::class.java)
30+
this.startService(intent)
31+
2632
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE, Manifest.permission.ANSWER_PHONE_CALLS), PERMISSION_REQUEST_READ_PHONE_STATE)
2733

2834
var sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
2935
var proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)
3036
var accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
3137

32-
mSensorEventListener = RaiseToAnswerSensorEventListener(sensorManager, proximitySensor, accelerometer)
33-
3438
val testButton: Button = findViewById(R.id.test_button)
3539
testButton.setOnClickListener {
3640
Toast.makeText(applicationContext, getString(R.string.hold_to_ear_test), Toast.LENGTH_SHORT).show()
37-
mSensorEventListener!!.waitUntilEarPickup {}
41+
RaiseToAnswerSensorEventListener.instance!!.waitUntilEarPickup { }
3842
}
3943

4044
val activeSwitch: Switch = findViewById(R.id.raise_to_answer_switch)
4145
val appEnabled = getSharedPreferences(getString(R.string.app_enabled_key), Context.MODE_PRIVATE)
4246

43-
activeSwitch.isChecked = appEnabled.getInt(getString(R.string.app_enabled_key), 1) == 1
44-
4547
activeSwitch.setOnCheckedChangeListener { _, isChecked ->
4648
if (isChecked) {
4749
with (appEnabled.edit()) {
@@ -54,7 +56,32 @@ class MainActivity : AppCompatActivity() {
5456
commit()
5557
}
5658
}
59+
setListenerState.push(isChecked)
5760
}
61+
62+
activeSwitch.isChecked = appEnabled.getInt(getString(R.string.app_enabled_key), 1) == 1
63+
64+
val executor = ScheduledThreadPoolExecutor(1)
65+
executor.scheduleWithFixedDelay({
66+
var listener: RaiseToAnswerSensorEventListener? = RaiseToAnswerSensorEventListener.instance
67+
68+
if (listener == null)
69+
return@scheduleWithFixedDelay
70+
71+
while (!setListenerState.empty()) {
72+
var value = setListenerState.pop()
73+
if (value) {
74+
listener.bind(
75+
this,
76+
sensorManager,
77+
proximitySensor,
78+
accelerometer
79+
)
80+
} else {
81+
listener.disable()
82+
}
83+
}
84+
}, 0L, 1000, TimeUnit.MILLISECONDS)
5885
}
5986

6087
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {

app/src/main/java/me/hackerchick/raisetoanswer/RaiseToAnswerCallReceiver.kt

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,13 @@ class RaiseToAnswerCallReceiver : BroadcastReceiver() {
1212
private var mTelephony: TelephonyManager? = null
1313
private var mPhoneListener: RaiseToAnswerPhoneStateListener? = null
1414

15-
private var mSensorManager: SensorManager? = null
16-
private var mProximitySensor: Sensor? = null
17-
private var mAccelerometer: Sensor? = null
18-
1915
override fun onReceive(context: Context, intent: Intent?) {
2016
if (context.getSharedPreferences(context.getString(R.string.app_enabled_key), Context.MODE_PRIVATE).getInt(context.getString(R.string.app_enabled_key), 1) != 1) {
2117
// Don't do anything if app is set to "disabled"
2218
return
2319
}
2420

25-
mSensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
26-
mProximitySensor = mSensorManager!!.getDefaultSensor(Sensor.TYPE_PROXIMITY)
27-
mAccelerometer = mSensorManager!!.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
28-
29-
mPhoneListener = RaiseToAnswerPhoneStateListener(context, mSensorManager!!, mProximitySensor!!, mAccelerometer!!)
21+
mPhoneListener = RaiseToAnswerPhoneStateListener(context)
3022

3123
mTelephony = context
3224
.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager

app/src/main/java/me/hackerchick/raisetoanswer/RaiseToAnswerPhoneStateListener.kt

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,39 @@
11
package me.hackerchick.raisetoanswer
22

33
import android.annotation.SuppressLint
4+
import android.app.Notification
5+
import android.app.NotificationChannel
6+
import android.app.PendingIntent
47
import android.content.Context
8+
import android.content.Intent
59
import android.hardware.Sensor
610
import android.hardware.SensorManager
711
import android.telecom.TelecomManager
812
import android.telephony.PhoneStateListener
913
import android.telephony.TelephonyManager
1014
import android.widget.Toast
1115

12-
class RaiseToAnswerPhoneStateListener(context: Context, sensorManager: SensorManager, proximitySensor: Sensor, accelerometer: Sensor) : PhoneStateListener() {
13-
private var mContext: Context? = null
14-
15-
private var mSensorEventListener: RaiseToAnswerSensorEventListener? = null
1616

17-
private var mSensorManager: SensorManager? = null
18-
private var mProximitySensor: Sensor? = null
19-
private var mAccelerometer: Sensor? = null
17+
class RaiseToAnswerPhoneStateListener(context: Context) : PhoneStateListener() {
18+
private var mContext: Context? = null
2019

2120
init {
2221
mContext = context
23-
mSensorManager = sensorManager
24-
mProximitySensor = proximitySensor
25-
mAccelerometer = accelerometer
2622
}
2723

2824
@SuppressLint("MissingPermission")
2925
override fun onCallStateChanged(state: Int, incomingNumber: String) {
3026
when (state) {
3127
TelephonyManager.CALL_STATE_IDLE -> {
32-
mSensorEventListener?.stop()
33-
mSensorEventListener = null
28+
RaiseToAnswerSensorEventListener.instance!!.stop()
3429
}
3530
TelephonyManager.CALL_STATE_RINGING -> {
36-
Toast.makeText(mContext, mContext!!.getString(R.string.hold_to_ear_to_answer), Toast.LENGTH_LONG).show()
37-
mSensorEventListener = RaiseToAnswerSensorEventListener(mSensorManager!!, mProximitySensor!!, mAccelerometer!!)
38-
mSensorEventListener!!.waitUntilEarPickup{ ->
31+
RaiseToAnswerSensorEventListener.instance!!.waitUntilEarPickup{ ->
3932
// Pickup triggered
4033
val tm = mContext!!.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
4134
tm.acceptRingingCall()
4235
}
36+
Toast.makeText(mContext, mContext!!.getString(R.string.hold_to_ear_to_answer), Toast.LENGTH_LONG).show()
4337
}
4438
}
4539
}

app/src/main/java/me/hackerchick/raisetoanswer/RaiseToAnswerSensorEventListener.kt

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,30 @@
11
package me.hackerchick.raisetoanswer
22

3+
import android.app.*
4+
import android.content.Context
5+
import android.content.Intent
36
import android.hardware.Sensor
47
import android.hardware.SensorEvent
58
import android.hardware.SensorEventListener
69
import android.hardware.SensorManager
710
import android.media.AudioManager
811
import android.media.ToneGenerator
12+
import android.os.IBinder
913
import android.util.Log
1014
import java.util.*
1115
import kotlin.math.atan2
1216
import kotlin.math.roundToInt
1317
import kotlin.math.sqrt
1418

15-
class RaiseToAnswerSensorEventListener(sensorManager: SensorManager, proximitySensor: Sensor, accelerometer: Sensor) : SensorEventListener {
19+
20+
class RaiseToAnswerSensorEventListener : Service(), SensorEventListener {
21+
companion object {
22+
var instance: RaiseToAnswerSensorEventListener? = null
23+
}
24+
25+
private val ONGOING_NOTIFICATION_ID = 1
1626
private val SENSOR_SENSITIVITY = 4
27+
1728
private var mProximityValue: Float? = null
1829
private var mInclinationValue: Int? = null
1930

@@ -25,14 +36,45 @@ class RaiseToAnswerSensorEventListener(sensorManager: SensorManager, proximitySe
2536
private var pickupBeepsDone = 0
2637
private var mTimer: Timer? = null
2738

39+
private var mContext: Context? = null
2840
private var mSensorManager: SensorManager? = null
2941
private var mProximitySensor: Sensor? = null
3042
private var mAccelerometer: Sensor? = null
3143

32-
init {
44+
override fun onCreate() {
45+
Log.d("TEST", "Creating")
46+
instance = this
47+
Log.d("TEST", "Created")
48+
}
49+
50+
override fun onBind(p0: Intent?): IBinder? { return null }
51+
52+
fun bind(context: Context, sensorManager: SensorManager, proximitySensor: Sensor, accelerometer: Sensor) {
53+
mContext = context
3354
mSensorManager = sensorManager
3455
mProximitySensor = proximitySensor
3556
mAccelerometer = accelerometer
57+
58+
val pendingIntent: PendingIntent =
59+
Intent(this, RaiseToAnswerSensorEventListener::class.java).let { notificationIntent ->
60+
PendingIntent.getActivity(mContext, 0, notificationIntent, 0)
61+
}
62+
63+
val channel = NotificationChannel("incoming_call", getString(R.string.incoming_call_service), NotificationManager.IMPORTANCE_LOW)
64+
val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
65+
service.createNotificationChannel(channel)
66+
67+
val notification: Notification = Notification.Builder(mContext, "incoming_call")
68+
.setSmallIcon(R.mipmap.ic_launcher)
69+
.setContentText(getText(R.string.raise_to_answer_is_enabled))
70+
.setContentIntent(pendingIntent)
71+
.build()
72+
73+
startForeground(ONGOING_NOTIFICATION_ID, notification)
74+
}
75+
76+
fun disable() {
77+
stopForeground(true)
3678
}
3779

3880
fun waitUntilEarPickup(callback: () -> Unit) {
@@ -45,6 +87,8 @@ class RaiseToAnswerSensorEventListener(sensorManager: SensorManager, proximitySe
4587
var proximityValue = mProximityValue
4688
var inclinationValue = mInclinationValue
4789

90+
Log.d("TESTTEST", inclinationValue.toString())
91+
4892
if (resetBeepsDone < 2) {
4993
if (proximityValue == null || (proximityValue >= SENSOR_SENSITIVITY || proximityValue <= -SENSOR_SENSITIVITY)) {
5094
mToneGenerator.startTone(ToneGenerator.TONE_CDMA_PIP, 100)

app/src/main/res/values-nl/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77
<string name="hold_to_ear_to_answer">Hou de telefoon tegen je oor om op te nemen</string>
88
<string name="raise_to_answer_header">Neem op met je oor</string>
99
<string name="raise_to_answer_explanation">Je zal 2 piepjes horen wanneer de app klaar is om een gesprek op te nemen. Hou dan de telefoon dan rechtop en tegen je oor aan om op te nemen. De app zal nog 3 maal piepen om de detectie te bevestigen.</string>
10+
<string name="incoming_call_service">Dienst voor inkomende gesprekken</string>
11+
<string name="raise_to_answer_is_enabled">Raise To Answer is ingeschakeld</string>
1012
</resources>

app/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77
<string name="hold_to_ear_to_answer">Hold your phone to your ear to answer</string>
88
<string name="raise_to_answer_header">Raise to Answer</string>
99
<string name="raise_to_answer_explanation">You will hear 2 beeps when the app is ready to take the call. Then, simply hold the phone to your ear in an upright position to answer the call. The app will beep 3 more times to signify it detecting proximity to your ear.</string>
10+
<string name="incoming_call_service">Incoming call service</string>
11+
<string name="raise_to_answer_is_enabled">Raise To Answer is enabled</string>
1012
</resources>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix answering not working if app is not in foreground
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Het beantwoorden van gesprekken terwijl de app niet in de voorgrond staat werkt nu

0 commit comments

Comments
 (0)