Branch.io sharesheet doesn't work, without any errors or bugs - branch

There no bug report I can attach. It just simply doesn't work. I click the share, and regardless which option I choose (copy, whatsapp, messanger etc) nothing happens. It just folds back to the bottom and does nothing. If I try to past after clicking the copy, it pastes the previous thing I copied (meaning, it didn't copy the share link at all).
This is my code:
lp = LinkProperties()
buo.listOnGoogleSearch(viewHolder.root.context)
share.setOnClickListener {
val ss = ShareSheetStyle(activity, "Republic invite", "Join me in this republic.")
.setCopyUrlStyle(activity.resources.getDrawable(android.R.drawable.ic_menu_send), "Copy", "Added to clipboard")
.setMoreOptionStyle(activity.resources.getDrawable(android.R.drawable.ic_menu_search), "Show more")
.addPreferredSharingOption(SharingHelper.SHARE_WITH.FACEBOOK)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.FACEBOOK_MESSENGER)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.WHATS_APP)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.TWITTER)
.setAsFullWidthStyle(true)
.setSharingTitle("Share With")
buo.showShareSheet(activity, lp, ss, object : Branch.BranchLinkShareListener {
override fun onShareLinkDialogLaunched() {}
override fun onShareLinkDialogDismissed() {}
override fun onLinkShareResponse(sharedLink: String, sharedChannel: String, error: BranchError) {}
override fun onChannelSelected(channelName: String) {
firebaseAnalytics.logEvent("community_shared_$channelName", null)
}
})

Can you please check if you see the same error after you change the codes as the following examples?
val buo = BranchUniversalObject()
val lp = LinkProperties()
val ss = ShareSheetStyle(this#MainActivity, "Check this out!", "This stuff is awesome: ")
.setCopyUrlStyle(
ContextCompat.getDrawable(this, android.R.drawable.ic_menu_send),
"Copy",
"Added to clipboard"
)
.setMoreOptionStyle(
ContextCompat.getDrawable(this, android.R.drawable.ic_menu_search),
"Show more"
)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.FACEBOOK)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.EMAIL)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.MESSAGE)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.HANGOUT)
.setAsFullWidthStyle(true)
.setSharingTitle("Share With")
buo.showShareSheet(this, lp, ss, object : Branch.BranchLinkShareListener {
override fun onShareLinkDialogLaunched() {}
override fun onShareLinkDialogDismissed() {}
override fun onLinkShareResponse(
sharedLink: String,
sharedChannel: String,
error: BranchError
) {
}
override fun onChannelSelected(channelName: String) {}
})
Please contact support#branch.io if you still need our assistance for this issue.

Related

Issue IDE warning if annotated member is not surrounded with a particular block

I have a data structure which has members that are not thread safe and the caller needs to lock the resource for reading and writing as appropriate. Here's a minimal code sample:
class ExampleResource : LockableProjectItem {
override val readWriteLock: ReadWriteLock = ReentrantReadWriteLock()
#RequiresReadLock
val nonThreadSafeMember: String = ""
}
interface LockableProjectItem {
val readWriteLock: ReadWriteLock
}
fun <T : LockableProjectItem, Out> T.readLock(block: T.() -> Out): Out {
try {
readWriteLock.readLock().lock()
return block(this)
} finally {
readWriteLock.readLock().unlock()
}
}
fun <T : LockableProjectItem, Out> T.writeLock(block: T.() -> Out): Out {
try {
readWriteLock.writeLock().lock()
return block(this)
} finally {
readWriteLock.writeLock().unlock()
}
}
annotation class RequiresReadLock
A call ExampleResource.nonThreadSafeMember might then look like this:
val resource = ExampleResource()
val readResult = resource.readLock { nonThreadSafeMember }
To make sure that the caller is aware that the resource needs to be locked, I would like the IDE to issue a warning for any members that are annotated with #RequiresReadLock and are not surrounded with a readLock block. Is there any way to do this in IntelliJ without writing a custom plugin for the IDE?
I think this is sort of a hack, but using context receivers might work. I don't think they are intended to be used in this way though.
You can declare a dummy object to act as the context receiver, and add that as a context receiver to the property:
object ReadLock
class ExampleResource : LockableProjectItem {
override val readWriteLock: ReadWriteLock = ReentrantReadWriteLock()
// properties with context receivers cannot have a backing field, so we need to explicitly declare this
private val nonThreadSafeMemberField: String = ""
context(ReadLock)
val nonThreadSafeMember: String
get() = nonThreadSafeMemberField
}
Then in readLock, you pass the object:
fun <T : LockableProjectItem, Out> T.readLock(block: context(ReadLock) T.() -> Out): Out {
try {
readWriteLock.readLock().lock()
return block(ReadLock, this)
} finally {
readWriteLock.readLock().unlock()
}
}
Notes:
This will give you an error if you try to access nonThreadSafeMember without the context receiver:
val resource = ExampleResource()
val readResult = resource.nonThreadSafeMember //error
You can still access nonThreadSafeMember without acquiring a read lock by doing e.g.
with(ReadLock) { // with(ReadLock) doesn't acquire the lock, just gets the context receiver
resource.nonThreadSafeMember // no error
}
But it's way harder to accidentally write something like this, which I think is what you are trying to prevent.
If you call another function inside readLock, and you want to access nonThreadSafeMember inside that function, you should mark that function with context(ReadLock) too. e.g.
fun main() {
val resource = ExampleResource()
val readResult = resource.readLock {
foo(this)
}
}
context(ReadLock)
fun foo(x: ExampleResource) {
x.nonThreadSafeMember
}
The context receiver is propagated through.

How do I make a generic function with generic function as parameter in Kotlin?

I have about 20 files that look like the Red and Blue section from the picture above, now I want to add
private val requestDataSource: RequestDataSource parameter (Green line 15) and call requestDataSource.post() (Green line 20) to all of them.
I've attempted to create a generic function with a custom parameter P, custom return type RET and a function parameter so I can use it as I do in the sendEz function but it's not working.
I am getting an error on the function parameter:
Type mismatch. Required: (TypeVariable(P)) → ApiRequestResponse<TypeVariable(RET)>
Found: Boolean
Function invocation 'send(...)' expected
I don't understand, I don't want to invoke it on line 30m and at the same time it looks like it's seen as invoked as the found parameter is a boolean (the return type of service.sen(emnail)) but I am not calling it there...
Is this achievable? If so, how?
Code here:
class EmailDataSource #Inject constructor(
private val dao: EmailDao,
private val service: EmailService,
private val requestDataSource: RequestDataSource,
): BaseDataSource<EmailModel, EmailDataModel>(dao) {
suspend fun send(email: EmailModel): ApiRequestResponse<Boolean> {
return try {
val response = service.send(email)
requestDataSource.post()
ApiRequestResponse.Success(response)
} catch (e: Throwable) {
val ioe = IOException("Error sending email", e)
Timber.w(ioe)
ApiRequestResponse.Error(ioe)
}
}
suspend fun sendEz(email: EmailModel): ApiRequestResponse<Boolean> {
return call<EmailModel, Boolean>(service.send, email, "Error sending email")
}
suspend fun <P: Any, RET: Any> call(
function: (parameter: P) -> ApiRequestResponse<RET>,
parameter: P,
error: String = "Error making the request"
): ApiRequestResponse<RET> {
return try {
val response = function(parameter)
requestDataSource.post()
response
} catch (e: Throwable) {
val ioe = IOException(error, e)
Timber.w(ioe)
ApiRequestResponse.Error(ioe)
}
}
}
Solution:
protected suspend fun <P: Any, RET: Any> call(
function: KSuspendFunction1<P, RET>,
parameter: P,
error: String = "Error making the request"
): ApiRequestResponse<RET> {
val result = function(parameter) // call the function
}
/** And call it like this */
suspend fun get(param: String, param2: Boolean): ApiRequestResponse<WHAT service::get RETURNS> {
return call(service::get, param, param2, "Error message")
}
/** Where service is the retrofit interface */
#GET("Endpoint/{date}/{includeStops}")
suspend fun get(
#Path("date") date: String,
#Path("stops") stops: Boolean
): List<Model>
A . is syntax for invoking it. :: is used for just passing references to functions.
So try putting service::send instead of service.send

Kotlin by lazy throws NullPointerException

I am currently trying to learn Kotlin with the help of the book "Kotlin Programming The Big Nerd Ranch Guide" and so far everything worked.
But now I am struggling with the "lazy" initialization which throws a NullPointerException which says
Cannot invoke "kotlin.Lazy.getValue()" because "< local1>" is null
The corresponding lines are:
val hometown by lazy { selectHometown() }
private fun selectHometown(): String = File("data/towns.txt").readText().split("\n").shuffled().first()
In case you want to compile it yourself or need more code for a better understanding I provide the Game.kt and Player.kt down below. If "lazy" is dropped for a "normal" initialization the hometown gets assigned as intended.
Any tips for solving the problem and understanding the cause of it is welcome.
// Game.kt
package com.bignerdranch.nyethack
fun main(args: Array<String>) {
val player = Player("Madrigal")
player.castFireball()
}
private fun printPlayerStatus(player: Player) {
println("(Aura: ${player.auraColor()}) " + "(Blessed: ${if (player.isBlessed) "YES" else "NO"})")
println("${player.name} ${player.formatHealthStatus()}")
}
// Player.kt
package com.bignerdranch.nyethack
import java.io.File
class Player(_name: String, var healthPoints: Int = 100, val isBlessed: Boolean, private val isImmortal: Boolean) {
var name = _name
get() = "${field.capitalize()} of $hometown"
private set(value) {
field = value.trim()
}
constructor(name: String) : this(name, isBlessed = true, isImmortal = false) {
if (name.toLowerCase() == "kar") healthPoints = 40
}
init {
require(healthPoints > 0, { "healthPoints must be greater than zero." })
require(name.isNotBlank(), { "Player must have a name" })
}
val hometown by lazy { selectHometown() }
private fun selectHometown(): String = File("data/towns.txt").readText().split("\n").shuffled().first()
fun castFireball(numFireballs: Int = 2) =
println("A glass of Fireball springs into existence. (x$numFireballs)")
fun auraColor(): String {
val auraVisible = isBlessed && healthPoints > 60 || isImmortal
return if (auraVisible) "GREEN" else "NONE"
}
fun formatHealthStatus() =
when (healthPoints) {
100 -> "is an excellent condition!"
in 90..99 -> "has a few scratches."
in 75..89 -> if (isBlessed) {
"has some minor wounds but is healing quite quickly"
} else {
"has some minor wounds"
}
in 15..74 -> "looks pretty hurt"
else -> "is in awful condition!"
}
}
I forgot the towns.txt so here it is (not that it matters much)
Neversummer
Abelhaven
Phandoril
Tampa
Sanorith
Trell
Zan'tro
Hermi Hermi
Curlthistle Forest
When something like this happens, it's usually due to bad ordering of initialization.
The initialization of the Player class goes this way:
the name property has its backing field initialized with the _name value
the init block is run, and tries to access name
the getter of name tries to read the hometown property, but fails because hometown is still not initialized
...if things had gone right, the hometown property would be initialized now with the lazy delegate
So basically you're trying to access hometown before the lazy delegate is configured.
If you move hometown's declaration above the init block, you should be fine.
You can see the fix in action on the playground

Kotlin - Trying to factorize code with high-order function

I'm quite new to Kotlin and I'd like to see if using high-order functions can help in my case.
My use-case is that I need to call the methods of an IInterface derived class to send events to one or more components. And I'd like to make this generic, and I want to check if a high-order funtion can help. A sample of code will help to understand (well, I hope so!).
private val eventListeners = mutableListOf<IEventInterface>() // List filled somewhere else!
private fun sendConnectionEvent(dummyString: String) {
val deadListeners = mutableListOf<IEventInterface>()
eventListeners.forEach {
try {
it.onConnectionEvent(dummyString)
} catch (e: DeadObjectException) {
Log.d(TAG, "Removing listener - Exception ${e.message}")
deadListeners.add(it)
}
}
deadListeners.forEach { it ->
eventListeners.remove(it)
}
}
private fun sendWonderfulEvent(dummyString: String, dummyInt: Int) {
val deadListeners = mutableListOf<IEventInterface>()
eventListeners.forEach {
try {
it.onWonderfulEvent(dummyString, dummyInt)
} catch (e: DeadObjectException) {
Log.d(TAG, "Removing listener - Exception ${e.message}")
deadListeners.add(it)
}
}
deadListeners.forEach { it ->
eventListeners.remove(it)
}
}
I added 2 similar methods (I will have many more in the real use case) and I think (I hope!) that something could be done but I can't make high-order function works in this case because:
I want to call the same method on several instances, and not 'just' a basic function
To make things even worse, the methods I need to call don't have the same prototype (that would have been too easy!).
Hope this is clear enough.
Thanks for your help!
VR
Here is how it can be done
fun onEvent(body: (IEventInterface) -> Unit) {
val deadListeners = mutableListOf<IEventInterface>()
eventListeners.forEach {
try {
body(it)
} catch (ex: DeadObjectException) {
Log.d(TAG, "Removing listener - Exception ${e.message}")
deadListeners.add(it)
}
}
deadListeners.forEach { it ->
eventListeners.remove(it)
}
}
Supposing an interface like this:
interface IEventInterface {
fun onConnectionEvent(dummyString: String)
fun onWonderfulEvent(dummyString: String, dummyInt: Int)
}
Define an generic type that implements your defined interface ( <T : IEventInterface>)
Define an mutable list of this type to receive your implementation (MutableList<T>.removeIfThrows)
Expect an extension function for you type that will do your specific validation (and custom parameters if you want)
Using an apply and returning the instance you can run your code like a pipeline
Executing the custom validation when you want
private fun <T : IEventInterface> MutableList<T>.removeIfThrows(validation: T.() -> Unit, customLogMessage: String? = null): MutableList<T> {
return apply {
removeIf {
it.runCatching {
validation()
}.onFailure { error ->
print(customLogMessage ?: "Removing listener - Exception ${error.message}")
}.isFailure
}
}
}
Define your specific implementation passing just the function with custom validation as an parameter
private fun <T : IEventInterface> MutableList<T>.sendConnectionEvent(dummyString: String) = removeIfThrows({
onConnectionEvent(dummyString)
})
private fun <T : IEventInterface> MutableList<T>.sendWonderfulEvent(dummyString: String, dummyInt: Int) = removeIfThrows({
onWonderfulEvent(dummyString, dummyInt)
})
Now you can run your code like an pipeline modifying your original object like this
private fun nowYouCanDoSomethingLikeThis() {
eventListeners
.sendConnectionEvent("some dummy string")
.sendWonderfulEvent("some another dummy string", 123)
}

When trying to implement Branch.io deep-linking using their share sheet, no action executes

I am trying to implement deep linking with branch and having issues with the Share Sheet. It just doesn't work. No matter what I click, the relevant action doesn't happen and it just goes back to the bottom of the screen. Even when I click copy, the text doesn't copy. there are no errors so I don't know what's wrong.
This is my code (it is a single item in a recyclerView. I am using GroupieAdapter):
class SingleCommunityOption(val community: Community, val activity : MainActivity) : Item<ViewHolder>() {
private lateinit var buo: BranchUniversalObject
private lateinit var lp: LinkProperties
override fun getLayout(): Int {
return R.layout.community_option_layout
}
override fun bind(viewHolder: ViewHolder, position: Int) {
val firebaseAnalytics = FirebaseAnalytics.getInstance(viewHolder.root.context!!)
val title = viewHolder.itemView.community_option_title
val description = viewHolder.itemView.community_option_description
val memberCount = viewHolder.itemView.community_option_members_count
val share= viewHolder.itemView.community_share
title.text = community.title
description.text = community.description
memberCount.text = "${community.members}"
buo = BranchUniversalObject()
.setCanonicalIdentifier(community.id)
.setTitle(community.title)
.setContentDescription("")
.setContentIndexingMode(BranchUniversalObject.CONTENT_INDEX_MODE.PUBLIC)
.setLocalIndexMode(BranchUniversalObject.CONTENT_INDEX_MODE.PUBLIC)
.setContentMetadata(ContentMetadata().addCustomMetadata("type", "community"))
lp = LinkProperties()
buo.listOnGoogleSearch(viewHolder.root.context)
share.setOnClickListener {
val ss = ShareSheetStyle(activity, "Republic invite", "Join me in this republic.")
.setCopyUrlStyle(activity.resources.getDrawable(android.R.drawable.ic_menu_send), "Copy", "Added to clipboard")
.setMoreOptionStyle(activity.resources.getDrawable(android.R.drawable.ic_menu_search), "Show more")
.addPreferredSharingOption(SharingHelper.SHARE_WITH.FACEBOOK)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.FACEBOOK_MESSENGER)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.WHATS_APP)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.TWITTER)
.setAsFullWidthStyle(true)
.setSharingTitle("Share With")
buo.showShareSheet(activity, lp, ss, object : Branch.BranchLinkShareListener {
override fun onShareLinkDialogLaunched() {}
override fun onShareLinkDialogDismissed() {}
override fun onLinkShareResponse(sharedLink: String, sharedChannel: String, error: BranchError) {}
override fun onChannelSelected(channelName: String) {
firebaseAnalytics.logEvent("community_shared_$channelName", null)
}
})
}
}
}