diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt index 14a23bacf..85a34536e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt @@ -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>> { + 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) } diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldAdapter.kt index 8e94c7063..710b0d9b8 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldAdapter.kt @@ -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() { var emojis: List = emptyList() - var fields: List = emptyList() + var fields: List> = emptyList() override fun getItemCount() = fields.size @@ -41,18 +44,30 @@ class AccountFieldAdapter(private val linkListener: LinkListener) : RecyclerView } override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { - val field = fields[position] + val proofOrField = fields[position] - val emojifiedName = CustomEmojiHelper.emojifyString(field.name, emojis, viewHolder.nameTextView) - viewHolder.nameTextView.text = emojifiedName + if(proofOrField.isLeft()) { + val identityProof = proofOrField.asLeft() - val emojifiedValue = CustomEmojiHelper.emojifyText(field.value, emojis, viewHolder.valueTextView) - LinkHelper.setClickableText(viewHolder.valueTextView, emojifiedValue, null, linkListener) + viewHolder.nameTextView.text = identityProof.provider + viewHolder.valueTextView.text = LinkHelper.createClickableText(identityProof.username, identityProof.profileUrl) + + viewHolder.valueTextView.movementMethod = LinkMovementMethod.getInstance() - if(field.verifiedAt != null) { viewHolder.valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_check_circle, 0) } else { - viewHolder.valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0 ) + val field = proofOrField.asRight() + val emojifiedName = CustomEmojiHelper.emojifyString(field.name, emojis, viewHolder.nameTextView) + viewHolder.nameTextView.text = emojifiedName + + val emojifiedValue = CustomEmojiHelper.emojifyText(field.value, emojis, viewHolder.valueTextView) + LinkHelper.setClickableText(viewHolder.valueTextView, emojifiedValue, null, linkListener) + + if(field.verifiedAt != null) { + viewHolder.valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_check_circle, 0) + } else { + viewHolder.valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0 ) + } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusDetailedViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusDetailedViewHolder.java index df0fb9231..b94b4cb71 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusDetailedViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusDetailedViewHolder.java @@ -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 { diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/IdentityProof.kt b/app/src/main/java/com/keylesspalace/tusky/entity/IdentityProof.kt new file mode 100644 index 000000000..9473f0372 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/entity/IdentityProof.kt @@ -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 +) diff --git a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt index 2fb3f9408..65d096e8d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt +++ b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt @@ -318,6 +318,11 @@ interface MastodonApi { @Query("id[]") accountIds: List ): Call> + @GET("api/v1/accounts/{id}/identity_proofs") + fun identityProofs( + @Path("id") accountId: String + ): Call> + @GET("api/v1/blocks") fun blocks( @Query("max_id") maxId: String? diff --git a/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.java b/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.java index bcc2ce5c3..85e6f2866 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.java @@ -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 * diff --git a/app/src/main/java/com/keylesspalace/tusky/viewmodel/AccountViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/viewmodel/AccountViewModel.kt index adbcaa43e..ad90cacc9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewmodel/AccountViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/viewmodel/AccountViewModel.kt @@ -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>() val relationshipData = MutableLiveData>() + private val identityProofData = MutableLiveData>() + + val accountFieldData = combineOptionalLiveData(accountData, identityProofData) { accountRes, identityProofs -> + identityProofs.orEmpty().map { Either.Left(it) } + .plus(accountRes?.data?.fields.orEmpty().map { Either.Right(it) }) + } + + private val callList: MutableList> = mutableListOf() private val disposable: Disposable = eventHub.events .subscribe { event -> @@ -60,6 +67,7 @@ class AccountViewModel @Inject constructor( } override fun onFailure(call: Call, 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>, 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> { + override fun onResponse(call: Call>, + response: Response>) { + val proofs = response.body() + if (response.isSuccessful && proofs != null ) { + identityProofData.postValue(proofs) + } else { + identityProofData.postValue(emptyList()) + } + } + + override fun onFailure(call: Call>, 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) }