Implement identity proof api (#1597)

* implement identity proof api

* fix warnings in AccountActivity

* fix createClickableText method

* improve error handling

* use combineOptionalLiveData to simplify code
This commit is contained in:
Konrad Pozniak 2019-12-21 18:56:16 +01:00 committed by GitHub
parent c253f6b23b
commit c44dd455b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 95 additions and 21 deletions

View file

@ -19,8 +19,7 @@ import android.animation.ArgbEvaluator
import android.content.Context
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.PorterDuff
import android.graphics.*
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
@ -52,6 +51,8 @@ import com.keylesspalace.tusky.components.compose.ComposeActivity
import com.keylesspalace.tusky.components.report.ReportActivity
import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.entity.Account
import com.keylesspalace.tusky.entity.Field
import com.keylesspalace.tusky.entity.IdentityProof
import com.keylesspalace.tusky.entity.Relationship
import com.keylesspalace.tusky.interfaces.ActionButtonActivity
import com.keylesspalace.tusky.interfaces.LinkListener
@ -118,7 +119,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
viewModel = ViewModelProviders.of(this, viewModelFactory)[AccountViewModel::class.java]
// Obtain information to fill out the profile.
viewModel.setAccountInfo(intent.getStringExtra(KEY_ACCOUNT_ID))
viewModel.setAccountInfo(intent.getStringExtra(KEY_ACCOUNT_ID)!!)
val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this)
animateAvatar = sharedPrefs.getBoolean("animateGifAvatars", false)
@ -350,6 +351,11 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
}
})
viewModel.accountFieldData.observe(this, Observer<List<Either<IdentityProof, Field>>> {
accountFieldAdapter.fields = it
accountFieldAdapter.notifyDataSetChanged()
})
}
/**
@ -378,7 +384,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
val emojifiedNote = CustomEmojiHelper.emojifyText(account.note, account.emojis, accountNoteTextView)
LinkHelper.setClickableText(accountNoteTextView, emojifiedNote, null, this)
accountFieldAdapter.fields = account.fields ?: emptyList()
// accountFieldAdapter.fields = account.fields ?: emptyList()
accountFieldAdapter.emojis = account.emojis ?: emptyList()
accountFieldAdapter.notifyDataSetChanged()
@ -472,7 +478,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
// this is necessary because API 19 can't handle vector compound drawables
val movedIcon = ContextCompat.getDrawable(this, R.drawable.ic_briefcase)?.mutate()
val textColor = ThemeUtils.getColor(this, android.R.attr.textColorTertiary)
movedIcon?.setColorFilter(textColor, PorterDuff.Mode.SRC_IN)
movedIcon?.colorFilter = PorterDuffColorFilter(textColor, PorterDuff.Mode.SRC_IN)
accountMovedText.setCompoundDrawablesRelativeWithIntrinsicBounds(movedIcon, null, null, null)
}

View file

@ -15,6 +15,7 @@
package com.keylesspalace.tusky.adapter
import android.text.method.LinkMovementMethod
import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.ViewGroup
@ -23,15 +24,17 @@ import android.widget.TextView
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.Emoji
import com.keylesspalace.tusky.entity.Field
import com.keylesspalace.tusky.entity.IdentityProof
import com.keylesspalace.tusky.interfaces.LinkListener
import com.keylesspalace.tusky.util.CustomEmojiHelper
import com.keylesspalace.tusky.util.Either
import com.keylesspalace.tusky.util.LinkHelper
import kotlinx.android.synthetic.main.item_account_field.view.*
class AccountFieldAdapter(private val linkListener: LinkListener) : RecyclerView.Adapter<AccountFieldAdapter.ViewHolder>() {
var emojis: List<Emoji> = emptyList()
var fields: List<Field> = emptyList()
var fields: List<Either<IdentityProof, Field>> = emptyList()
override fun getItemCount() = fields.size
@ -41,8 +44,19 @@ class AccountFieldAdapter(private val linkListener: LinkListener) : RecyclerView
}
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
val field = fields[position]
val proofOrField = fields[position]
if(proofOrField.isLeft()) {
val identityProof = proofOrField.asLeft()
viewHolder.nameTextView.text = identityProof.provider
viewHolder.valueTextView.text = LinkHelper.createClickableText(identityProof.username, identityProof.profileUrl)
viewHolder.valueTextView.movementMethod = LinkMovementMethod.getInstance()
viewHolder.valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_check_circle, 0)
} else {
val field = proofOrField.asRight()
val emojifiedName = CustomEmojiHelper.emojifyString(field.name, emojis, viewHolder.nameTextView)
viewHolder.nameTextView.text = emojifiedName
@ -54,6 +68,7 @@ class AccountFieldAdapter(private val linkListener: LinkListener) : RecyclerView
} else {
viewHolder.valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0 )
}
}
}

View file

@ -115,10 +115,7 @@ class StatusDetailedViewHolder extends StatusBaseViewHolder {
timestampInfo.append("");
if (app.getWebsite() != null) {
URLSpan span = new CustomURLSpan(app.getWebsite());
SpannableStringBuilder text = new SpannableStringBuilder(app.getName());
text.setSpan(span, 0, app.getName().length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
CharSequence text = LinkHelper.createClickableText(app.getName(), app.getWebsite());
timestampInfo.append(text);
timestampInfo.setMovementMethod(LinkMovementMethod.getInstance());
} else {

View file

@ -0,0 +1,9 @@
package com.keylesspalace.tusky.entity
import com.google.gson.annotations.SerializedName
data class IdentityProof(
val provider: String,
@SerializedName("provider_username") val username: String,
@SerializedName("profile_url") val profileUrl: String
)

View file

@ -318,6 +318,11 @@ interface MastodonApi {
@Query("id[]") accountIds: List<String>
): Call<List<Relationship>>
@GET("api/v1/accounts/{id}/identity_proofs")
fun identityProofs(
@Path("id") accountId: String
): Call<List<IdentityProof>>
@GET("api/v1/blocks")
fun blocks(
@Query("max_id") maxId: String?

View file

@ -179,6 +179,14 @@ public class LinkHelper {
view.setMovementMethod(LinkMovementMethod.getInstance());
}
public static CharSequence createClickableText(String text, String link) {
URLSpan span = new CustomURLSpan(link);
SpannableStringBuilder clickableText = new SpannableStringBuilder(text);
clickableText.setSpan(span, 0, text.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
return clickableText;
}
/**
* Opens a link, depending on the settings, either in the browser or in a custom tab
*

View file

@ -6,12 +6,11 @@ import androidx.lifecycle.ViewModel
import com.keylesspalace.tusky.appstore.*
import com.keylesspalace.tusky.db.AccountManager
import com.keylesspalace.tusky.entity.Account
import com.keylesspalace.tusky.entity.Field
import com.keylesspalace.tusky.entity.IdentityProof
import com.keylesspalace.tusky.entity.Relationship
import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.Error
import com.keylesspalace.tusky.util.Loading
import com.keylesspalace.tusky.util.Resource
import com.keylesspalace.tusky.util.Success
import com.keylesspalace.tusky.util.*
import io.reactivex.disposables.Disposable
import retrofit2.Call
import retrofit2.Callback
@ -27,6 +26,14 @@ class AccountViewModel @Inject constructor(
val accountData = MutableLiveData<Resource<Account>>()
val relationshipData = MutableLiveData<Resource<Relationship>>()
private val identityProofData = MutableLiveData<List<IdentityProof>>()
val accountFieldData = combineOptionalLiveData(accountData, identityProofData) { accountRes, identityProofs ->
identityProofs.orEmpty().map { Either.Left<IdentityProof, Field>(it) }
.plus(accountRes?.data?.fields.orEmpty().map { Either.Right<IdentityProof, Field>(it) })
}
private val callList: MutableList<Call<*>> = mutableListOf()
private val disposable: Disposable = eventHub.events
.subscribe { event ->
@ -60,6 +67,7 @@ class AccountViewModel @Inject constructor(
}
override fun onFailure(call: Call<Account>, t: Throwable) {
Log.w(TAG, "failed obtaining account", t)
accountData.postValue(Error())
isDataLoading = false
isRefreshing.postValue(false)
@ -90,6 +98,7 @@ class AccountViewModel @Inject constructor(
}
override fun onFailure(call: Call<List<Relationship>>, t: Throwable) {
Log.w(TAG, "failed obtaining relationships", t)
relationshipData.postValue(Error())
}
})
@ -98,6 +107,30 @@ class AccountViewModel @Inject constructor(
}
}
private fun obtainIdentityProof(reload: Boolean = false) {
if (identityProofData.value == null || reload) {
val call = mastodonApi.identityProofs(accountId)
call.enqueue(object : Callback<List<IdentityProof>> {
override fun onResponse(call: Call<List<IdentityProof>>,
response: Response<List<IdentityProof>>) {
val proofs = response.body()
if (response.isSuccessful && proofs != null ) {
identityProofData.postValue(proofs)
} else {
identityProofData.postValue(emptyList())
}
}
override fun onFailure(call: Call<List<IdentityProof>>, t: Throwable) {
Log.w(TAG, "failed obtaining identity proofs", t)
}
})
callList.add(call)
}
}
fun changeFollowState() {
val relationship = relationshipData.value?.data
if (relationship?.following == true || relationship?.requested == true) {
@ -227,6 +260,7 @@ class AccountViewModel @Inject constructor(
return
accountId.let {
obtainAccount(isReload)
obtainIdentityProof()
if (!isSelf)
obtainRelationship(isReload)
}