Add expand/collapse button for threads (#609)
This commit is contained in:
parent
3dfe43dfb2
commit
f3c6abdd4d
8 changed files with 108 additions and 26 deletions
|
@ -35,9 +35,17 @@ import dagger.android.support.HasSupportFragmentInjector;
|
|||
|
||||
public class ViewThreadActivity extends BaseActivity implements HasSupportFragmentInjector {
|
||||
|
||||
public static final int REVEAL_BUTTON_HIDDEN = 1;
|
||||
public static final int REVEAL_BUTTON_REVEAL = 2;
|
||||
public static final int REVEAL_BUTTON_HIDE = 3;
|
||||
|
||||
private int revealButtonState = REVEAL_BUTTON_HIDDEN;
|
||||
|
||||
@Inject
|
||||
public DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;
|
||||
|
||||
private ViewThreadFragment fragment;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
@ -54,7 +62,7 @@ public class ViewThreadActivity extends BaseActivity implements HasSupportFragme
|
|||
|
||||
String id = getIntent().getStringExtra("id");
|
||||
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
|
||||
Fragment fragment = ViewThreadFragment.newInstance(id);
|
||||
fragment = ViewThreadFragment.newInstance(id);
|
||||
fragmentTransaction.replace(R.id.fragment_container, fragment);
|
||||
fragmentTransaction.commit();
|
||||
}
|
||||
|
@ -62,9 +70,26 @@ public class ViewThreadActivity extends BaseActivity implements HasSupportFragme
|
|||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.view_thread_toolbar, menu);
|
||||
MenuItem menuItem = menu.findItem(R.id.action_reveal);
|
||||
menuItem.setVisible(revealButtonState != REVEAL_BUTTON_HIDDEN);
|
||||
menuItem.setIcon(revealButtonState == REVEAL_BUTTON_REVEAL ?
|
||||
R.drawable.ic_eye_24dp : R.drawable.ic_hide_media_24dp);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
public void setRevealButtonState(int state) {
|
||||
switch (state) {
|
||||
case REVEAL_BUTTON_HIDDEN:
|
||||
case REVEAL_BUTTON_REVEAL:
|
||||
case REVEAL_BUTTON_HIDE:
|
||||
this.revealButtonState = state;
|
||||
invalidateOptionsMenu();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid reveal button state: " + state);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
|
@ -76,6 +101,10 @@ public class ViewThreadActivity extends BaseActivity implements HasSupportFragme
|
|||
LinkHelper.openLink(getIntent().getStringExtra("url"), this);
|
||||
return true;
|
||||
}
|
||||
case R.id.action_reveal: {
|
||||
fragment.onRevealPressed();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
|
|
@ -393,18 +393,15 @@ abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
contentWarningDescription.setVisibility(View.VISIBLE);
|
||||
contentWarningButton.setVisibility(View.VISIBLE);
|
||||
contentWarningButton.setChecked(expanded);
|
||||
contentWarningButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
contentWarningDescription.invalidate();
|
||||
if (getAdapterPosition() != RecyclerView.NO_POSITION) {
|
||||
listener.onExpandedChange(isChecked, getAdapterPosition());
|
||||
}
|
||||
if (isChecked) {
|
||||
content.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
content.setVisibility(View.GONE);
|
||||
}
|
||||
contentWarningButton.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
contentWarningDescription.invalidate();
|
||||
if (getAdapterPosition() != RecyclerView.NO_POSITION) {
|
||||
listener.onExpandedChange(isChecked, getAdapterPosition());
|
||||
}
|
||||
if (isChecked) {
|
||||
content.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
content.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
if (expanded) {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
package com.keylesspalace.tusky.adapter;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -45,7 +46,7 @@ public class ThreadAdapter extends RecyclerView.Adapter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
switch (viewType) {
|
||||
default:
|
||||
case VIEW_TYPE_STATUS: {
|
||||
|
@ -62,7 +63,7 @@ public class ThreadAdapter extends RecyclerView.Adapter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
|
||||
StatusViewData.Concrete status = statuses.get(position);
|
||||
if (position == detailedStatusPosition) {
|
||||
StatusDetailedViewHolder holder = (StatusDetailedViewHolder) viewHolder;
|
||||
|
|
|
@ -36,6 +36,7 @@ import android.view.ViewGroup;
|
|||
|
||||
import com.keylesspalace.tusky.BuildConfig;
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.ViewThreadActivity;
|
||||
import com.keylesspalace.tusky.adapter.ThreadAdapter;
|
||||
import com.keylesspalace.tusky.di.Injectable;
|
||||
import com.keylesspalace.tusky.entity.Attachment;
|
||||
|
@ -61,7 +62,7 @@ import retrofit2.Call;
|
|||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
public class ViewThreadFragment extends SFragment implements
|
||||
public final class ViewThreadFragment extends SFragment implements
|
||||
SwipeRefreshLayout.OnRefreshListener, StatusActionListener, Injectable {
|
||||
private static final String TAG = "ViewThreadFragment";
|
||||
|
||||
|
@ -78,7 +79,7 @@ public class ViewThreadFragment extends SFragment implements
|
|||
|
||||
private int statusIndex = 0;
|
||||
|
||||
private PairedList<Status, StatusViewData.Concrete> statuses =
|
||||
private final PairedList<Status, StatusViewData.Concrete> statuses =
|
||||
new PairedList<>(new Function<Status, StatusViewData.Concrete>() {
|
||||
@Override
|
||||
public StatusViewData.Concrete apply(Status input) {
|
||||
|
@ -109,7 +110,8 @@ public class ViewThreadFragment extends SFragment implements
|
|||
swipeRefreshLayout = rootView.findViewById(R.id.swipe_refresh_layout);
|
||||
swipeRefreshLayout.setOnRefreshListener(this);
|
||||
swipeRefreshLayout.setColorSchemeResources(R.color.primary);
|
||||
swipeRefreshLayout.setProgressBackgroundColorSchemeColor(ThemeUtils.getColor(context, android.R.attr.colorBackground));
|
||||
swipeRefreshLayout.setProgressBackgroundColorSchemeColor(
|
||||
ThemeUtils.getColor(context, android.R.attr.colorBackground));
|
||||
|
||||
recyclerView = rootView.findViewById(R.id.recycler_view);
|
||||
recyclerView.setHasFixedSize(true);
|
||||
|
@ -158,6 +160,29 @@ public class ViewThreadFragment extends SFragment implements
|
|||
onRefresh();
|
||||
}
|
||||
|
||||
public void onRevealPressed() {
|
||||
boolean allExpanded = allExpanded();
|
||||
for (int i = 0; i < statuses.size(); i++) {
|
||||
StatusViewData.Concrete newViewData =
|
||||
new StatusViewData.Concrete.Builder(statuses.getPairedItem(i))
|
||||
.setIsExpanded(!allExpanded)
|
||||
.createStatusViewData();
|
||||
statuses.setPairedItem(i, newViewData);
|
||||
}
|
||||
adapter.setStatuses(statuses.getPairedCopy());
|
||||
}
|
||||
|
||||
private boolean allExpanded() {
|
||||
boolean allExpanded = true;
|
||||
for (int i = 0; i < statuses.size(); i++) {
|
||||
if (!statuses.getPairedItem(i).isExpanded()) {
|
||||
allExpanded = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return allExpanded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
sendStatusRequest(thisThreadsStatusId);
|
||||
|
@ -271,6 +296,7 @@ public class ViewThreadFragment extends SFragment implements
|
|||
.createStatusViewData();
|
||||
statuses.setPairedItem(position, newViewData);
|
||||
adapter.setItem(position, newViewData, false);
|
||||
updateRevealIcon();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -400,13 +426,10 @@ public class ViewThreadFragment extends SFragment implements
|
|||
swipeRefreshLayout.setRefreshing(false);
|
||||
if (view != null) {
|
||||
Snackbar.make(view, R.string.error_generic, Snackbar.LENGTH_LONG)
|
||||
.setAction(R.string.action_retry, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
sendThreadRequest(id);
|
||||
sendStatusRequest(id);
|
||||
sendCardRequest(id);
|
||||
}
|
||||
.setAction(R.string.action_retry, v -> {
|
||||
sendThreadRequest(id);
|
||||
sendStatusRequest(id);
|
||||
sendCardRequest(id);
|
||||
})
|
||||
.show();
|
||||
} else {
|
||||
|
@ -433,6 +456,7 @@ public class ViewThreadFragment extends SFragment implements
|
|||
}
|
||||
statuses.setPairedItem(i, viewData);
|
||||
adapter.addItem(i, viewData);
|
||||
updateRevealIcon();
|
||||
return i;
|
||||
}
|
||||
|
||||
|
@ -492,6 +516,7 @@ public class ViewThreadFragment extends SFragment implements
|
|||
throw new AssertionError(error);
|
||||
}
|
||||
adapter.addAll(descendantsViewData);
|
||||
updateRevealIcon();
|
||||
}
|
||||
|
||||
private void showCard(Card card) {
|
||||
|
@ -511,4 +536,25 @@ public class ViewThreadFragment extends SFragment implements
|
|||
statuses.clear();
|
||||
adapter.clear();
|
||||
}
|
||||
|
||||
private void updateRevealIcon() {
|
||||
ViewThreadActivity activity = ((ViewThreadActivity) getActivity());
|
||||
if (activity == null) return;
|
||||
|
||||
boolean hasAnyWarnings = false;
|
||||
// Statuses are updated from the main thread so nothing should change while iterating
|
||||
for (int i = 0; i < statuses.size(); i++) {
|
||||
if (statuses.get(i).getSpoilerText() != null
|
||||
&& !statuses.get(i).getSpoilerText().isEmpty()) {
|
||||
hasAnyWarnings = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasAnyWarnings) {
|
||||
activity.setRevealButtonState(ViewThreadActivity.REVEAL_BUTTON_HIDDEN);
|
||||
return;
|
||||
}
|
||||
activity.setRevealButtonState(allExpanded() ? ViewThreadActivity.REVEAL_BUTTON_HIDE :
|
||||
ViewThreadActivity.REVEAL_BUTTON_REVEAL);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,4 +7,11 @@
|
|||
android:title="@string/action_open_in_web"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_reveal"
|
||||
android:title="@string/expand_collapse_all_statuses"
|
||||
app:showAsAction="ifRoom"
|
||||
android:icon="@drawable/ic_eye_24dp" />
|
||||
|
||||
|
||||
</menu>
|
|
@ -287,5 +287,6 @@
|
|||
<string name="send_toot_notification_channel_name">Отправка постов</string>
|
||||
<string name="send_toot_notification_cancel_title">Отправка отменена</string>
|
||||
<string name="send_toot_notification_saved_content">Копия поста сохранена в ваши черновики</string>
|
||||
<string name="expand_collapse_all_statuses">Раскрыть/свернуть все статусы</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -304,5 +304,6 @@
|
|||
<string name="error_no_custom_emojis">Your instance %s does not have any custom emojis</string>
|
||||
<string name="copy_to_clipboard_success">Copied to clipboard</string>
|
||||
<string name="performing_lookup_title">Performing lookup...</string>
|
||||
<string name="expand_collapse_all_statuses">Expand/Collapse all statuses</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -7,7 +7,7 @@ buildscript {
|
|||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.1.1'
|
||||
classpath 'com.android.tools.build:gradle:3.1.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue