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