Account page interactions with locked users are made much clearer.

main
Vavassor 8 years ago
parent 5943c051f5
commit 9f925d7f1c
  1. 152
      app/src/main/java/com/keylesspalace/tusky/AccountActivity.java
  2. 9
      app/src/main/res/drawable/ic_hourglass_24dp.xml
  3. 4
      app/src/main/res/values/strings.xml

@ -15,7 +15,9 @@
package com.keylesspalace.tusky; package com.keylesspalace.tusky;
import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
@ -57,16 +59,26 @@ import retrofit2.Response;
public class AccountActivity extends BaseActivity implements SFragment.OnUserRemovedListener { public class AccountActivity extends BaseActivity implements SFragment.OnUserRemovedListener {
private static final String TAG = "AccountActivity"; // logging tag private static final String TAG = "AccountActivity"; // logging tag
private enum FollowState {
NOT_FOLLOWING,
FOLLOWING,
REQUESTED,
}
private String accountId; private String accountId;
private boolean following = false; private FollowState followState;
private boolean blocking = false; private boolean blocking;
private boolean muting = false; private boolean muting;
private boolean isSelf; private boolean isSelf;
private TabLayout tabLayout;
private AccountPagerAdapter pagerAdapter; private AccountPagerAdapter pagerAdapter;
private Account loadedAccount; private Account loadedAccount;
@BindView(R.id.account_avatar) CircularImageView avatar;
@BindView(R.id.account_header) ImageView header;
@BindView(R.id.floating_btn) FloatingActionButton floatingBtn;
@BindView(R.id.tab_layout) TabLayout tabLayout;
@BindView(R.id.account_locked) ImageView accountLockedView; @BindView(R.id.account_locked) ImageView accountLockedView;
@BindView(R.id.activity_account) View container;
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
@ -76,19 +88,25 @@ public class AccountActivity extends BaseActivity implements SFragment.OnUserRem
if (savedInstanceState != null) { if (savedInstanceState != null) {
accountId = savedInstanceState.getString("accountId"); accountId = savedInstanceState.getString("accountId");
followState = (FollowState) savedInstanceState.getSerializable("followState");
blocking = savedInstanceState.getBoolean("blocking");
muting = savedInstanceState.getBoolean("muting");
} else { } else {
Intent intent = getIntent(); Intent intent = getIntent();
accountId = intent.getStringExtra("id"); accountId = intent.getStringExtra("id");
followState = FollowState.NOT_FOLLOWING;
blocking = false;
muting = false;
} }
loadedAccount = null;
SharedPreferences preferences = getPrivatePreferences(); SharedPreferences preferences = getPrivatePreferences();
String loggedInAccountId = preferences.getString("loggedInAccountId", null); String loggedInAccountId = preferences.getString("loggedInAccountId", null);
// Setup the toolbar.
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar(); ActionBar actionBar = getSupportActionBar();
if (actionBar != null) { if (actionBar != null) {
actionBar.setTitle(null); actionBar.setTitle(null);
actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setDisplayHomeAsUpEnabled(true);
@ -120,14 +138,13 @@ public class AccountActivity extends BaseActivity implements SFragment.OnUserRem
} }
}); });
FloatingActionButton floatingBtn = (FloatingActionButton) findViewById(R.id.floating_btn); // Initialise the default UI states.
floatingBtn.hide(); floatingBtn.hide();
CircularImageView avatar = (CircularImageView) findViewById(R.id.account_avatar);
ImageView header = (ImageView) findViewById(R.id.account_header);
avatar.setImageResource(R.drawable.avatar_default); avatar.setImageResource(R.drawable.avatar_default);
header.setImageResource(R.drawable.account_header_default); header.setImageResource(R.drawable.account_header_default);
// Obtain information to fill out the profile.
obtainAccount(); obtainAccount();
if (!accountId.equals(loggedInAccountId)) { if (!accountId.equals(loggedInAccountId)) {
isSelf = false; isSelf = false;
@ -156,7 +173,6 @@ public class AccountActivity extends BaseActivity implements SFragment.OnUserRem
R.drawable.tab_page_margin_dark); R.drawable.tab_page_margin_dark);
viewPager.setPageMarginDrawable(pageMarginDrawable); viewPager.setPageMarginDrawable(pageMarginDrawable);
viewPager.setAdapter(adapter); viewPager.setAdapter(adapter);
tabLayout = (TabLayout) findViewById(R.id.tab_layout);
tabLayout.setupWithViewPager(viewPager); tabLayout.setupWithViewPager(viewPager);
for (int i = 0; i < tabLayout.getTabCount(); i++) { for (int i = 0; i < tabLayout.getTabCount(); i++) {
TabLayout.Tab tab = tabLayout.getTabAt(i); TabLayout.Tab tab = tabLayout.getTabAt(i);
@ -169,13 +185,16 @@ public class AccountActivity extends BaseActivity implements SFragment.OnUserRem
@Override @Override
protected void onSaveInstanceState(Bundle outState) { protected void onSaveInstanceState(Bundle outState) {
outState.putString("accountId", accountId); outState.putString("accountId", accountId);
outState.putSerializable("followState", followState);
outState.putBoolean("blocking", blocking);
outState.putBoolean("muting", muting);
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
} }
private void obtainAccount() { private void obtainAccount() {
mastodonAPI.account(accountId).enqueue(new Callback<Account>() { mastodonAPI.account(accountId).enqueue(new Callback<Account>() {
@Override @Override
public void onResponse(Call<Account> call, retrofit2.Response<Account> response) { public void onResponse(Call<Account> call, Response<Account> response) {
if (response.isSuccessful()) { if (response.isSuccessful()) {
onObtainAccountSuccess(response.body()); onObtainAccountSuccess(response.body());
} else { } else {
@ -196,8 +215,6 @@ public class AccountActivity extends BaseActivity implements SFragment.OnUserRem
TextView username = (TextView) findViewById(R.id.account_username); TextView username = (TextView) findViewById(R.id.account_username);
TextView displayName = (TextView) findViewById(R.id.account_display_name); TextView displayName = (TextView) findViewById(R.id.account_display_name);
TextView note = (TextView) findViewById(R.id.account_note); TextView note = (TextView) findViewById(R.id.account_note);
CircularImageView avatar = (CircularImageView) findViewById(R.id.account_avatar);
ImageView header = (ImageView) findViewById(R.id.account_header);
String usernameFormatted = String.format( String usernameFormatted = String.format(
getString(R.string.status_username_format), account.username); getString(R.string.status_username_format), account.username);
@ -274,10 +291,12 @@ public class AccountActivity extends BaseActivity implements SFragment.OnUserRem
ids.add(accountId); ids.add(accountId);
mastodonAPI.relationships(ids).enqueue(new Callback<List<Relationship>>() { mastodonAPI.relationships(ids).enqueue(new Callback<List<Relationship>>() {
@Override @Override
public void onResponse(Call<List<Relationship>> call, retrofit2.Response<List<Relationship>> response) { public void onResponse(Call<List<Relationship>> call,
Response<List<Relationship>> response) {
if (response.isSuccessful()) { if (response.isSuccessful()) {
Relationship relationship = response.body().get(0); Relationship relationship = response.body().get(0);
onObtainRelationshipsSuccess(relationship.following, relationship.blocking, relationship.muting); onObtainRelationshipsSuccess(relationship.requested, relationship.following,
relationship.blocking, relationship.muting);
} else { } else {
onObtainRelationshipsFailure(new Exception(response.message())); onObtainRelationshipsFailure(new Exception(response.message()));
} }
@ -290,12 +309,19 @@ public class AccountActivity extends BaseActivity implements SFragment.OnUserRem
}); });
} }
private void onObtainRelationshipsSuccess(boolean following, boolean blocking, boolean muting) { private void onObtainRelationshipsSuccess(boolean followRequested, boolean following,
this.following = following; boolean blocking, boolean muting) {
if (following) {
followState = FollowState.FOLLOWING;
} else if (followRequested) {
followState = FollowState.REQUESTED;
} else {
followState = FollowState.NOT_FOLLOWING;
}
this.blocking = blocking; this.blocking = blocking;
this.muting = muting; this.muting = muting;
if (!following || !blocking || !muting) { if (followState != FollowState.NOT_FOLLOWING || !blocking || !muting) {
invalidateOptionsMenu(); invalidateOptionsMenu();
} }
@ -313,20 +339,28 @@ public class AccountActivity extends BaseActivity implements SFragment.OnUserRem
} }
private void updateFollowButton(FloatingActionButton button) { private void updateFollowButton(FloatingActionButton button) {
if (following) { switch (followState) {
button.setImageResource(R.drawable.ic_person_minus_24px); case NOT_FOLLOWING: {
button.setContentDescription(getString(R.string.action_unfollow));
} else {
button.setImageResource(R.drawable.ic_person_add_24dp); button.setImageResource(R.drawable.ic_person_add_24dp);
button.setContentDescription(getString(R.string.action_follow)); button.setContentDescription(getString(R.string.action_follow));
break;
}
case REQUESTED: {
button.setImageResource(R.drawable.ic_hourglass_24dp);
button.setContentDescription(getString(R.string.state_follow_requested));
break;
}
case FOLLOWING: {
button.setImageResource(R.drawable.ic_person_minus_24px);
button.setContentDescription(getString(R.string.action_unfollow));
break;
}
} }
} }
private void updateButtons() { private void updateButtons() {
invalidateOptionsMenu(); invalidateOptionsMenu();
final FloatingActionButton floatingBtn = (FloatingActionButton) findViewById(R.id.floating_btn);
if(!isSelf && !blocking) { if(!isSelf && !blocking) {
floatingBtn.show(); floatingBtn.show();
@ -335,7 +369,11 @@ public class AccountActivity extends BaseActivity implements SFragment.OnUserRem
floatingBtn.setOnClickListener(new View.OnClickListener() { floatingBtn.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (followState != FollowState.REQUESTED) {
follow(accountId); follow(accountId);
} else {
showFollowRequestPendingDialog(accountId);
}
updateFollowButton(floatingBtn); updateFollowButton(floatingBtn);
} }
}); });
@ -352,18 +390,24 @@ public class AccountActivity extends BaseActivity implements SFragment.OnUserRem
return super.onCreateOptionsMenu(menu); return super.onCreateOptionsMenu(menu);
} }
private String getFollowAction() {
switch (followState) {
default:
case NOT_FOLLOWING: return getString(R.string.action_follow);
case REQUESTED:
case FOLLOWING: return getString(R.string.action_unfollow);
}
}
@Override @Override
public boolean onPrepareOptionsMenu(Menu menu) { public boolean onPrepareOptionsMenu(Menu menu) {
if (!isSelf) { if (!isSelf) {
MenuItem follow = menu.findItem(R.id.action_follow); MenuItem follow = menu.findItem(R.id.action_follow);
String title; follow.setTitle(getFollowAction());
if (following) { follow.setVisible(followState != FollowState.REQUESTED);
title = getString(R.string.action_unfollow);
} else {
title = getString(R.string.action_follow);
}
follow.setTitle(title);
MenuItem block = menu.findItem(R.id.action_block); MenuItem block = menu.findItem(R.id.action_block);
String title;
if (blocking) { if (blocking) {
title = getString(R.string.action_unblock); title = getString(R.string.action_unblock);
} else { } else {
@ -389,10 +433,18 @@ public class AccountActivity extends BaseActivity implements SFragment.OnUserRem
private void follow(final String id) { private void follow(final String id) {
Callback<Relationship> cb = new Callback<Relationship>() { Callback<Relationship> cb = new Callback<Relationship>() {
@Override @Override
public void onResponse(Call<Relationship> call, retrofit2.Response<Relationship> response) { public void onResponse(Call<Relationship> call, Response<Relationship> response) {
if (response.isSuccessful()) { if (response.isSuccessful()) {
following = response.body().following; Relationship relationship = response.body();
// TODO: display message/indicator when "requested" is true (i.e. when the follow is awaiting approval) if (relationship.following) {
followState = FollowState.FOLLOWING;
} else if (relationship.requested) {
followState = FollowState.REQUESTED;
Snackbar.make(container, R.string.state_follow_requested,
Snackbar.LENGTH_LONG).show();
} else {
followState = FollowState.NOT_FOLLOWING;
}
updateButtons(); updateButtons();
} else { } else {
onFollowFailure(id); onFollowFailure(id);
@ -405,10 +457,10 @@ public class AccountActivity extends BaseActivity implements SFragment.OnUserRem
} }
}; };
if (following) { Assert.expect(followState != FollowState.REQUESTED);
mastodonAPI.unfollowAccount(id).enqueue(cb); switch (followState) {
} else { case NOT_FOLLOWING: { mastodonAPI.followAccount(id).enqueue(cb); break; }
mastodonAPI.followAccount(id).enqueue(cb); case FOLLOWING: { mastodonAPI.unfollowAccount(id).enqueue(cb); break; }
} }
} }
@ -419,16 +471,28 @@ public class AccountActivity extends BaseActivity implements SFragment.OnUserRem
follow(id); follow(id);
} }
}; };
View anyView = findViewById(R.id.activity_account); Snackbar.make(container, R.string.error_generic, Snackbar.LENGTH_LONG)
Snackbar.make(anyView, R.string.error_generic, Snackbar.LENGTH_LONG)
.setAction(R.string.action_retry, listener) .setAction(R.string.action_retry, listener)
.show(); .show();
} }
private void showFollowRequestPendingDialog(final String id) {
DialogInterface.OnClickListener waitListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
};
new AlertDialog.Builder(AccountActivity.this)
.setMessage(R.string.dialog_message_follow_request)
.setPositiveButton(R.string.action_ok, waitListener)
.show();
}
private void block(final String id) { private void block(final String id) {
Callback<Relationship> cb = new Callback<Relationship>() { Callback<Relationship> cb = new Callback<Relationship>() {
@Override @Override
public void onResponse(Call<Relationship> call, retrofit2.Response<Relationship> response) { public void onResponse(Call<Relationship> call, Response<Relationship> response) {
if (response.isSuccessful()) { if (response.isSuccessful()) {
blocking = response.body().blocking; blocking = response.body().blocking;
updateButtons(); updateButtons();
@ -456,8 +520,7 @@ public class AccountActivity extends BaseActivity implements SFragment.OnUserRem
block(id); block(id);
} }
}; };
View anyView = findViewById(R.id.activity_account); Snackbar.make(container, R.string.error_generic, Snackbar.LENGTH_LONG)
Snackbar.make(anyView, R.string.error_generic, Snackbar.LENGTH_LONG)
.setAction(R.string.action_retry, listener) .setAction(R.string.action_retry, listener)
.show(); .show();
} }
@ -495,8 +558,7 @@ public class AccountActivity extends BaseActivity implements SFragment.OnUserRem
mute(id); mute(id);
} }
}; };
View anyView = findViewById(R.id.activity_account); Snackbar.make(container, R.string.error_generic, Snackbar.LENGTH_LONG)
Snackbar.make(anyView, R.string.error_generic, Snackbar.LENGTH_LONG)
.setAction(R.string.action_retry, listener) .setAction(R.string.action_retry, listener)
.show(); .show();
} }

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M6,2v6h0.01L6,8.01 10,12l-4,4 0.01,0.01L6,16.01L6,22h12v-5.99h-0.01L18,16l-4,-4 4,-3.99 -0.01,-0.01L18,8L18,2L6,2zM16,16.5L16,20L8,20v-3.5l4,-4 4,4zM12,11.5l-4,-4L8,4h8v3.5l-4,4z"/>
</vector>

@ -14,7 +14,6 @@
<string name="error_media_upload_opening">That file could not be opened.</string> <string name="error_media_upload_opening">That file could not be opened.</string>
<string name="error_media_upload_permission">Permission to read media is required.</string> <string name="error_media_upload_permission">Permission to read media is required.</string>
<string name="error_media_download_permission">Permission to store media is required.</string> <string name="error_media_download_permission">Permission to store media is required.</string>
<string name="error_media_upload_image_or_video">Images and videos cannot both be attached to the same status.</string> <string name="error_media_upload_image_or_video">Images and videos cannot both be attached to the same status.</string>
<string name="error_media_upload_sending">The upload failed.</string> <string name="error_media_upload_sending">The upload failed.</string>
<string name="error_report_too_few_statuses">At least one status must be reported.</string> <string name="error_report_too_few_statuses">At least one status must be reported.</string>
@ -133,6 +132,7 @@
<string name="dialog_title_finishing_media_upload">Finishing Media Upload</string> <string name="dialog_title_finishing_media_upload">Finishing Media Upload</string>
<string name="dialog_message_uploading_media">Uploading…</string> <string name="dialog_message_uploading_media">Uploading…</string>
<string name="dialog_download_image">Download</string> <string name="dialog_download_image">Download</string>
<string name="dialog_message_follow_request">Follow request pending: awaiting their response</string>
<string name="visibility_public">Public: Post to public timelines</string> <string name="visibility_public">Public: Post to public timelines</string>
<string name="visibility_unlisted">Unlisted: Do not show in public timelines</string> <string name="visibility_unlisted">Unlisted: Do not show in public timelines</string>
@ -166,4 +166,6 @@
<string name="description_account_locked">Locked Account</string> <string name="description_account_locked">Locked Account</string>
<string name="status_share_content">Share content of toot</string> <string name="status_share_content">Share content of toot</string>
<string name="status_share_link">Share link to toot</string> <string name="status_share_link">Share link to toot</string>
<string name="state_follow_requested">Follow requested</string>
</resources> </resources>

Loading…
Cancel
Save