Trying to create Endpoints using ARN and device token.
For generating the endpoints I have used the below class in Android Application.
I am not receiving notification from AWS SNS, is there anything that I'm missing to add in the code?
To generate device token I have added this line.
var token = FirebaseInstanceId.getInstance().token
class EndpointRegistration {
companion object {
var client = AmazonSNSClient() //provide credentials here
val awsCredentials: AWSCredentials = BasicAWSCredentials(BuildConfig.AWS_ACCESS, BuildConfig.AWS_SECRET_KEY)
var arnStorage: String? = null
fun registerWithSNS(userToken: String, context: Context) {
var endpointArn: String? = retrieveEndpointArn()
val token = userToken
var updateNeeded = false
var createNeeded = null == endpointArn
if (createNeeded) {
// No platform endpoint ARN is stored; need to call createEndpoint.
endpointArn = createEndpoint(context, token)
createNeeded = false
}
println("Retrieving platform endpoint data...")
// Look up the platform endpoint and make sure the data in it is current, even if
// it was just created.
try {
val geaReq = GetEndpointAttributesRequest()
.withEndpointArn(endpointArn)
val geaRes: GetEndpointAttributesResult = client.getEndpointAttributes(geaReq)
updateNeeded = (geaRes.attributes["Token"] != token
|| !geaRes.attributes["Enabled"].equals("true", ignoreCase = true))
} catch (nfe: NotFoundException) {
// We had a stored ARN, but the platform endpoint associated with it
// disappeared. Recreate it.
createNeeded = true
}
if (createNeeded) {
createEndpoint(context, token)
}
println("updateNeeded = $updateNeeded")
if (updateNeeded) {
// The platform endpoint is out of sync with the current data;
// update the token and enable it.
println("Updating platform endpoint $endpointArn")
val attribs: MutableMap<String, String> = HashMap()
attribs["Token"] = token
attribs["Enabled"] = "true"
val saeReq = SetEndpointAttributesRequest()
.withEndpointArn(endpointArn)
.withAttributes(attribs)
client.setEndpointAttributes(saeReq)
Log.d("Updatingd", "=" + client.endpointPrefix)
}
}
private fun createEndpoint(context: Context, token: String): String? {
var endpointArn: String? = null
endpointArn = try {
println("Creating platform endpoint with token $token")
client = AmazonSNSClient(awsCredentials)
client.setRegion(Region.getRegion(Regions.EU_WEST_1));
val cpeReq = CreatePlatformEndpointRequest()
.withPlatformApplicationArn(BuildConfig.PLATFORM_ARN)
.withToken(token)
val cpeRes: CreatePlatformEndpointResult = client
.createPlatformEndpoint(cpeReq)
cpeRes.endpointArn
} catch (ipe: InvalidParameterException) {
val message: String = ipe.getErrorMessage()
println("Exception message: $message")
val p: Pattern = Pattern
.compile(".*Endpoint (arn:aws:sns[^ ]+) already exists " +
"with the same [Tt]oken.*")
val m: Matcher = p.matcher(message)
if (m.matches()) {
// The platform endpoint already exists for this token, but with
// additional custom data that
// createEndpoint doesn't want to overwrite. Just use the
// existing platform endpoint.
m.group(1)
} else {
// Rethrow the exception, the input is actually bad.
throw ipe
}
}
storeEndpointArn(endpointArn!!)
return endpointArn
}
/**
* #return the ARN the app was registered under previously, or null if no
* platform endpoint ARN is stored.
*/
private fun retrieveEndpointArn(): String? {
// Retrieve the platform endpoint ARN from permanent storage,
// or return null if null is stored.
return arnStorage
}
/**
* Stores the platform endpoint ARN in permanent storage for lookup next time.
*/
private fun storeEndpointArn(endpointArn: String) {
// Write the platform endpoint ARN to permanent storage.
arnStorage = endpointArn
}
}
}
Related
I've seen that there are ways to update an app with Firebase Remote Config. Some sort of "Force Update" Notification. If anyone can explain it to me, that would be great.
How to use Firebase to update your Android App?
There are multiple ways in which you can update an Android app. The first one would be to store data in a database. Firebase has two real-time databases, Cloud Firestore and the Realtime Database. You can one or the other, according to the use case of your app. For that I recommend you check the following resource:
https://firebase.google.com/docs/database/rtdb-vs-firestore
When it comes to Remote Config, please notice that nowadays you can propagate Remote Config updates in real-time. That being said, there is no need to force anything. So I highly recommend that a look at that.
For Force update in a simple case the idea is
with firebase remort config sends the version number which you want for your application to be forced
then compare remort version with the local application version
if there is a mismatch then show a permanent dialog (cancelable=false) with a button when the user clicks on that button to open the application in the play store .
Check out this Small Class created for force update with remort config
class ForceUpdateChecker(private val context: Context, private val onUpdateNeededListener: OnUpdateNeededListener?) {
interface OnUpdateNeededListener {
fun onUpdateNeeded(updateUrl: String?)
}
fun check() {
val remoteConfig = FirebaseRemoteConfig.getInstance()
if (remoteConfig.getBoolean(KEY_UPDATE_REQUIRED)) {
val currentVersion = remoteConfig.getString(KEY_CURRENT_VERSION)
val appVersion = getAppVersion(context)
val updateUrl = remoteConfig.getString(KEY_UPDATE_URL)
if (!TextUtils.equals(currentVersion, appVersion)
&& onUpdateNeededListener != null
) {
onUpdateNeededListener.onUpdateNeeded(updateUrl)
}
}
}
private fun getAppVersion(context: Context): String {
var result = ""
try {
result = context.packageManager
.getPackageInfo(context.packageName, 0).versionName
result = result.replace("[a-zA-Z]|-".toRegex(), "")
} catch (e: PackageManager.NameNotFoundException) {
Log.e(TAG, e.message!!)
}
return result
}
class Builder(private val context: Context) {
private var onUpdateNeededListener: OnUpdateNeededListener? = null
fun onUpdateNeeded(onUpdateNeededListener: OnUpdateNeededListener?): Builder {
this.onUpdateNeededListener = onUpdateNeededListener
return this
}
fun build(): ForceUpdateChecker {
return ForceUpdateChecker(context, onUpdateNeededListener)
}
fun check(): ForceUpdateChecker {
val forceUpdateChecker = build()
forceUpdateChecker.check()
return forceUpdateChecker
}
}
companion object {
private val TAG = ForceUpdateChecker::class.java.simpleName
const val KEY_UPDATE_REQUIRED = "force_update_required"
const val KEY_CURRENT_VERSION = "force_update_current_version"
const val KEY_UPDATE_URL = "force_update_store_url"
fun with(context: Context): Builder {
return Builder(context)
}
}}
Call this like this in baseActivity (or from your landing page just not in splash screen)
ForceUpdateChecker.with(this).onUpdateNeeded(this).check();
In application on create add this
val firebaseRemoteConfig = FirebaseRemoteConfig.getInstance()
// set in-app defaults
val remoteConfigDefaults: MutableMap<String, Any> = HashMap()
remoteConfigDefaults[ForceUpdateChecker.KEY_UPDATE_REQUIRED] = false
remoteConfigDefaults[ForceUpdateChecker.KEY_CURRENT_VERSION] = "1.0"
remoteConfigDefaults[ForceUpdateChecker.KEY_UPDATE_URL] =
"https://play.google.com/store/apps/details?id=com.com.classified.pems"
firebaseRemoteConfig.setDefaultsAsync(remoteConfigDefaults)
firebaseRemoteConfig.fetch(60) // fetch every minutes
.addOnCompleteListener { task ->
if (task.isSuccessful) {
Log.d(TAG, "remote config is fetched.")
firebaseRemoteConfig.fetchAndActivate()
}
}
I am trying authenticate user with spotify app and spotify auth API (implementation 'com.spotify.android:auth:2.0.1') followed the steps mentioned in Spotify SDK github sample
my code:
Added in gradle(app.module) defaultConfig { manifestPlaceholders = [redirectSchemeName: "appname", redirectHostName:"spotify_login_callback"] }
// Fragment/Activity
val CLIENT_ID = "7bf56252cd644b339cc97df5b4d7eeee"
val AUTH_TOKEN_REQUEST_CODE = 0x10
val AUTH_CODE_REQUEST_CODE = 0x11
var mAccessToken: String? = null
var mAccessCode: String? = null
fun onRequestTokenClicked() {
val request = getAuthenticationRequest(AuthorizationResponse.Type.TOKEN)
AuthorizationClient.openLoginActivity(requireActivity(), AUTH_TOKEN_REQUEST_CODE, request)
}
fun onRequestCodeClicked() {
val request: AuthorizationRequest =
getAuthenticationRequest(AuthorizationResponse.Type.CODE)
AuthorizationClient.openLoginActivity(requireActivity(), AUTH_CODE_REQUEST_CODE, request)
}
private fun getAuthenticationRequest(type: AuthorizationResponse.Type): AuthorizationRequest {
return AuthorizationRequest.Builder(
CLIENT_ID,
type,
getRedirectUri().toString()
)
.setShowDialog(false)
// "user-read-email"
.setScopes(arrayOf("user-read-email")) // user-read-private , "streaming"
.build()
}
private fun getRedirectUri(): Uri? {
return Uri.Builder()
.scheme("appname")
.authority("spotify_login_callback")
.build()
}
val response = AuthorizationClient.getResponse(resultCode, data)
if (response.error != null && !response.error.isEmpty()) {
setResponse(response.error)
Toast.makeText(requireActivity(),"Error: response.error"+response.error,Toast.LENGTH_SHORT).show()
}
if (requestCode == AUTH_TOKEN_REQUEST_CODE) {
mAccessToken = response.accessToken
Toast.makeText(requireActivity(),"AccessToken: "+mAccessToken,Toast.LENGTH_SHORT).show()
updateTokenView()
} else if (requestCode == AUTH_CODE_REQUEST_CODE) {
mAccessCode = response.code
Toast.makeText(requireActivity(),"AccessCode"+mAccessCode,Toast.LENGTH_SHORT).show()
}
This code prints log "Spotify auth completing. The response is in EXTRA with key response" after debugging library gives AUTHENTICATION SERVICE UNKNOWN_ERROR does anyone know the cause of this error, same code provided in SDK sample works fine.
I am trying to call get api using an AWS signing method but not able to get the response.
Below is my code.
val secretkey = "E+t5/nDf6/NKNJBjbsdjv"
val accesskey = "DJKSBDKSBNKFGNBFG"
val credentials: AWSCredentials = BasicAWSCredentials(accesskey, secretkey)
val API_GATEWAY_SERVICE_NAME = "s3"
val requestAws: Request<*> = DefaultRequest<Any?>(API_GATEWAY_SERVICE_NAME)
val uri = URI.create("https://s3.us-west-2.amazonaws.com/..../../sample")
requestAws.endpoint = uri
requestAws.resourcePath = "https://s3.us-west-2.amazonaws.com/..../../sample"
requestAws.httpMethod = HttpMethodName.GET
val signer = AWS4Signer() signer . setServiceName (API_GATEWAY_SERVICE_NAME)
signer.setRegionName("us-west-2")
signer.sign(requestAws, credentials)
val headers = requestAws.headers
val key: MutableList<String> = ArrayList()
val value: MutableList<String> = ArrayList()
for ((key1, value1) in headers)
{
key.add(key1) value . add (value1)
}
val httpClient = OkHttpClient()
val request: okhttp3.Request = okhttp3.Request.Builder()
.url("https://s3.us-west-2.amazonaws.com/..../../sample")
.addHeader(key[0], value[0])
.addHeader(key[1], value[1])
.addHeader(key[2], value[2])
.addHeader("X-Amz-Content-Sha256",
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
.build()
val response: okhttp3.Response = httpClient.newCall(request).execute()
Log.i("LOG", response.body.toString())
Not able to figure out, what I am doing mistake.
Please help me out with this issue.
If you want to create an Android app written in Kotlin and invokes AWS Services, use the AWS SDK for Kotlin.
This SDK has strongly typed Service Clients that you can use in an Android Studio project that lets you invoke a given service. (as opposed to using okhttp3.Request, etc)
For example, here is Kotlin code that invoke SNS using a Strongly typed service client named SnsClient.
// Get all subscriptions.
fun getSubs(view: View) = runBlocking {
val subList = mutableListOf<String>()
val snsClient: SnsClient = getClient()
try {
val request = ListSubscriptionsByTopicRequest {
topicArn = topicArnVal
}
val response = snsClient.listSubscriptionsByTopic(request)
response.subscriptions?.forEach { sub ->
subList.add(sub.endpoint.toString())
}
val listString = java.lang.String.join(", ", subList)
showToast(listString)
} catch (e: SnsException) {
println(e.message)
snsClient.close()
}
}
fun getClient() : SnsClient{
val staticCredentials = StaticCredentialsProvider {
accessKeyId = "<Enter key>"
secretAccessKey = "<Enter key>"
}
val snsClient = SnsClient{
region = "us-west-2"
credentialsProvider = staticCredentials
}
return snsClient
}
TO learn how to use the AWS SDK for Kotlin, see
AWS SDK for Kotlin Developer Guide
I have a variable loginToken. I want to assign value to is as
var loginToken: String = getLoginToken()
Following is my getLoginToken function
private fun getLoginToken(context: Context): String {
val sharedPref: SharedPreferences = context.getSharedPreferences(PREF_NAME, PRIVATE_MODE)
var token = sharedPref.getString("token", null)
if (token == null) {
token = LoginManager(activity).getToken(onGetSuccess = {
// I get token here, i want to assign it to token var
}, onGetFailure = {
// I want to set token as empty string
})
}
return token
}
How should I assign value to token and finally return it only when I get the result of getToken call?
You can use suspendCancellableCoroutine to convert a callback approach to coroutines. So for a coroutine approach, we can do something like this:
private fun getLoginToken(context: Context): String {
val sharedPref: SharedPreferences = context.getSharedPreferences(PREF_NAME, PRIVATE_MODE)
var token = sharedPref.getString("token", null)
if (token == null) {
token = runBlocking { getTokenFromActivity(activity) }
}
return token
}
private suspend fun getTokenFromActivity(activity: Activity) =
suspendCancellableCoroutine {
LoginManager(activity).getToken(onGetSuccess = {
it.completeResume(result)
}, onGetFailure = {
it.completeResume("")
})
}
You can also use a locking approach. For example, using a CountdownLatch. (lock it before the getToken function and unlock when the result is ready in the callback.)
I am new to Android and Kotlin and am currently working on a centralized API router class.
To achieve this I am using the Fuel Framework.
For the doAsync function, I use the Anko for Kotlin library.
To retrieve an authorization token from the API I currently use this method:
private fun Login(username: String, password: String, callback: (Map<Boolean, String>) -> Unit) {
"/auth/token.json".httpPost()
.header(mapOf("Content-Type" to "application/json"))
.body("""{"username":"$username", "password":"$password"}""", Charsets.UTF_8)
.response { request, response, result ->
request.headers.remove("Accept-Encoding")
when (result) {
is Result.Failure -> {
// val data = result.get()
val ex = result.getException()
val serverResponseJson = response.data.toString(Charsets.UTF_8)
var exceptionMessage = ex.message
val jelement = JsonParser().parse(serverResponseJson)
val jobject = jelement.asJsonObject
val serverResponseError = if (jobject.has("Error")) jobject.get("Error").asString else jobject.get("detail").asString
callback(mapOf(Pair(false, serverResponseError)))
}
is Result.Success -> {
val data = result.get()
val returnJson = data.toString(Charsets.UTF_8)
Log.println(Log.ASSERT, "RESULT_LOGIN", returnJson)
callback(mapOf(Pair(true, returnJson)))
}
}
}
}
I invoke this login method at
val btnLogin = findViewById<Button>(R.id.btn_login)
btnLogin.setOnClickListener { _ ->
doAsync {
val username = findViewById<EditText>(R.id.input_username_login)
val password = findViewById<EditText>(R.id.input_password_login)
Login(username.text.toString(), password.text.toString()) {
// Request was successful
if (it.containsKey(true)) {
// Parse return Json
// e.g. {"id":"36e8fac0-487a-11e8-ad4e-c471feb11e42","token":"d6897a230fd7739e601649bf5fd89ea4b93317f6","expiry":"2018-04-27T17:49:48.721278Z"}
val jelement = JsonParser().parse(it.getValue(true))
val jobject = jelement.asJsonObject
// save field for class-scope access
Constants.token = jobject.get("token").asString
Constants.id = jobject.get("id").asString
}
else{
Toast.makeText(this#LoginActivity, it.getValue(false), Toast.LENGTH_SHORT).show()
}
}
}[30, TimeUnit.SECONDS]
var test = Constants.id;
}
In a separate Constants class, I store the token and id like this:
class Constants {
companion object {
val baseUrl: String = "BASE_URL_TO_MY_API"
val contentTypeJson = "application/json"
lateinit var STOREAGE_PATH: String
// current user details
lateinit var id: String
lateinit var token: String
lateinit var refresh_token: String
// logged in User
lateinit var user: User
}
How do I make sure that the test variable is set after the asynchronous task is done? Currently, I run into
lateinit property id has not been initialized
I have come across the option to limit the task to a timeout such as I have done with [30, TimeUnit.SECONDS], unfortunately, this did not help.
Thanks for the help! Cheers.
I think the problem is where you want to access the result:
val btnLogin = findViewById<Button>(R.id.btn_login)
btnLogin.setOnClickListener { _ ->
doAsync {
val username = findViewById<EditText>(R.id.input_username_login)
val password = findViewById<EditText>(R.id.input_password_login)
var test: String? = null
Login(username.text.toString(), password.text.toString()) {
// Request was successful
if (it.containsKey(true)) {
// Parse return Json
// e.g. {"id":"36e8fac0-487a-11e8-ad4e-c471feb11e42","token":"d6897a230fd7739e601649bf5fd89ea4b93317f6","expiry":"2018-04-27T17:49:48.721278Z"}
val jelement = JsonParser().parse(it.getValue(true))
val jobject = jelement.asJsonObject
// save field for class-scope access
Constants.token = jobject.get("token").asString
Constants.id = jobject.get("id").asString
}
else{
Toast.makeText(this#LoginActivity, it.getValue(false), Toast.LENGTH_SHORT).show()
}
}
test = Constants.id // here test variable surely set if result was successful, otherwise it holds the null value
test?.let{
resultDelivered(it)
}
}[30, TimeUnit.SECONDS]
}
fun resultDelivered(id: String){
// here we know that the async job has successfully finished
}