1 package host.exp.exponent.notifications 2 3 import android.app.IntentService 4 import android.content.Intent 5 import android.util.Log 6 import com.facebook.soloader.SoLoader 7 import host.exp.exponent.analytics.EXL 8 import host.exp.exponent.di.NativeModuleDepsProvider 9 import host.exp.exponent.kernel.ExponentUrls 10 import host.exp.exponent.network.ExpoHttpCallback 11 import host.exp.exponent.network.ExpoResponse 12 import host.exp.exponent.network.ExponentNetwork 13 import host.exp.exponent.storage.ExponentSharedPreferences 14 import host.exp.exponent.utils.AsyncCondition 15 import okhttp3.MediaType.Companion.toMediaTypeOrNull 16 import okhttp3.RequestBody.Companion.toRequestBody 17 import org.json.JSONException 18 import org.json.JSONObject 19 import java.io.IOException 20 import javax.inject.Inject 21 22 abstract class ExponentNotificationIntentService(name: String?) : IntentService(name) { 23 @Throws(IOException::class) getTokennull24 abstract fun getToken(): String? 25 26 abstract fun getSharedPrefsKey(): ExponentSharedPreferences.ExponentSharedPreferencesKey 27 28 abstract fun getServerType(): String 29 30 @Inject 31 lateinit var exponentSharedPreferences: ExponentSharedPreferences 32 33 @Inject 34 lateinit var exponentNetwork: ExponentNetwork 35 36 var isInitialized = false 37 38 private fun initialize() { 39 if (isInitialized) { 40 return 41 } 42 try { 43 NativeModuleDepsProvider.instance.inject(ExponentNotificationIntentService::class.java, this) 44 isInitialized = true 45 } catch (e: Throwable) { 46 } 47 } 48 onCreatenull49 override fun onCreate() { 50 super.onCreate() 51 initialize() 52 } 53 54 /* 55 * This function MUST set AsyncCondition.notify(DEVICE_PUSH_TOKEN_KEY); 56 * eventually. Otherwise it's possible for us to get in a state where 57 * the AsyncCondition listeners are never called (and the promise from 58 * getExpoPushTokenAsync never resolves). 59 */ onHandleIntentnull60 override fun onHandleIntent(intent: Intent?) { 61 initialize() 62 try { 63 val token = getToken() 64 if (token == null) { 65 setTokenError("Device push token is null") 66 return 67 } 68 69 val sharedPreferencesToken = exponentSharedPreferences.getString(getSharedPrefsKey()) 70 if (sharedPreferencesToken == token) { 71 // Server already has this token, don't need to send it again. 72 AsyncCondition.notify(DEVICE_PUSH_TOKEN_KEY) 73 return 74 } 75 76 // Needed for Arguments.createMap 77 SoLoader.init(this, false) 78 79 val uuid = exponentSharedPreferences.getOrCreateUUID() 80 try { 81 val params = JSONObject().apply { 82 put("deviceToken", token) 83 put("deviceId", uuid) 84 put("appId", applicationContext.packageName) 85 put("type", getServerType()) 86 } 87 88 val body = params.toString().toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()) 89 val request = ExponentUrls.addExponentHeadersToUrl("https://exp.host/--/api/v2/push/updateDeviceToken") 90 .header("Content-Type", "application/json") 91 .post(body) 92 .build() 93 94 exponentNetwork.client.call( 95 request, 96 object : ExpoHttpCallback { 97 override fun onFailure(e: IOException) { 98 setTokenError(e) 99 } 100 101 @Throws(IOException::class) 102 override fun onResponse(response: ExpoResponse) { 103 if (!response.isSuccessful) { 104 setTokenError("Failed to update the native device token with the Expo push notification service") 105 return 106 } 107 exponentSharedPreferences.setString(getSharedPrefsKey(), token) 108 hasTokenError = false 109 AsyncCondition.notify(DEVICE_PUSH_TOKEN_KEY) 110 } 111 } 112 ) 113 Log.i(TAG, "${getServerType()} Registration Token: $token") 114 } catch (e: JSONException) { 115 setTokenError(e) 116 } 117 } catch (e: SecurityException) { 118 setTokenError("Are you running in Genymotion? Follow this guide https://inthecheesefactory.com/blog/how-to-install-google-services-on-genymotion/en to install Google Play Services") 119 } catch (e: IOException) { 120 setTokenError(e) 121 } 122 } 123 setTokenErrornull124 private fun setTokenError(e: Exception) { 125 hasTokenError = true 126 AsyncCondition.notify(DEVICE_PUSH_TOKEN_KEY) 127 EXL.e(TAG, e) 128 } 129 setTokenErrornull130 private fun setTokenError(message: String) { 131 hasTokenError = true 132 AsyncCondition.notify(DEVICE_PUSH_TOKEN_KEY) 133 EXL.e(TAG, message) 134 } 135 136 companion object { 137 private val TAG = ExponentNotificationIntentService::class.java.simpleName 138 139 const val DEVICE_PUSH_TOKEN_KEY = "devicePushToken" 140 141 var hasTokenError = false 142 private set 143 } 144 } 145