diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a929daa3..7491beee 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -38,6 +38,7 @@
+
accounts;
private AccountActionListener accountActionListener;
private FooterActionListener footerActionListener;
+ private FooterViewHolder.State footerState;
public AccountAdapter(AccountActionListener accountActionListener,
FooterActionListener footerActionListener) {
@@ -42,6 +43,7 @@ public class AccountAdapter extends RecyclerView.Adapter {
accounts = new ArrayList<>();
this.accountActionListener = accountActionListener;
this.footerActionListener = footerActionListener;
+ footerState = FooterViewHolder.State.LOADING;
}
@Override
@@ -69,6 +71,7 @@ public class AccountAdapter extends RecyclerView.Adapter {
holder.setupActionListener(accountActionListener);
} else {
FooterViewHolder holder = (FooterViewHolder) viewHolder;
+ holder.setState(footerState);
holder.setupButton(footerActionListener);
holder.setRetryMessage(R.string.footer_retry_accounts);
holder.setEndOfTimelineMessage(R.string.footer_end_of_accounts);
@@ -116,6 +119,10 @@ public class AccountAdapter extends RecyclerView.Adapter {
return null;
}
+ public void setFooterState(FooterViewHolder.State state) {
+ this.footerState = state;
+ }
+
private static class AccountViewHolder extends RecyclerView.ViewHolder {
private View container;
private TextView username;
diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountFragment.java b/app/src/main/java/com/keylesspalace/tusky/AccountFragment.java
index 05131b8a..1742188d 100644
--- a/app/src/main/java/com/keylesspalace/tusky/AccountFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/AccountFragment.java
@@ -23,7 +23,6 @@ import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
-import android.support.v4.content.ContextCompat;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
@@ -45,12 +44,12 @@ import java.util.Map;
public class AccountFragment extends Fragment implements AccountActionListener,
FooterActionListener {
- private static final String TAG = "Account";
- private static int EXPECTED_ACCOUNTS_FETCHED = 20;
+ private static final String TAG = "Account"; // logging tag
public enum Type {
FOLLOWS,
FOLLOWERS,
+ BLOCKS,
}
private Type type;
@@ -63,6 +62,14 @@ public class AccountFragment extends Fragment implements AccountActionListener,
private AccountAdapter adapter;
private TabLayout.OnTabSelectedListener onTabSelectedListener;
+ public static AccountFragment newInstance(Type type) {
+ Bundle arguments = new Bundle();
+ AccountFragment fragment = new AccountFragment();
+ arguments.putString("type", type.name());
+ fragment.setArguments(arguments);
+ return fragment;
+ }
+
public static AccountFragment newInstance(Type type, String accountId) {
Bundle arguments = new Bundle();
AccountFragment fragment = new AccountFragment();
@@ -99,7 +106,8 @@ public class AccountFragment extends Fragment implements AccountActionListener,
recyclerView.setLayoutManager(layoutManager);
DividerItemDecoration divider = new DividerItemDecoration(
context, layoutManager.getOrientation());
- Drawable drawable = ContextCompat.getDrawable(context, R.drawable.status_divider_dark);
+ Drawable drawable = ThemeUtils.getDrawable(context, R.attr.status_divider_drawable,
+ R.drawable.status_divider_dark);
divider.setDrawable(drawable);
recyclerView.addItemDecoration(divider);
scrollListener = new EndlessOnScrollListener(layoutManager) {
@@ -118,45 +126,54 @@ public class AccountFragment extends Fragment implements AccountActionListener,
adapter = new AccountAdapter(this, this);
recyclerView.setAdapter(adapter);
- TabLayout layout = (TabLayout) getActivity().findViewById(R.id.tab_layout);
- onTabSelectedListener = new TabLayout.OnTabSelectedListener() {
- @Override
- public void onTabSelected(TabLayout.Tab tab) {}
+ if (jumpToTopAllowed()) {
+ TabLayout layout = (TabLayout) getActivity().findViewById(R.id.tab_layout);
+ onTabSelectedListener = new TabLayout.OnTabSelectedListener() {
+ @Override
+ public void onTabSelected(TabLayout.Tab tab) {
+ }
- @Override
- public void onTabUnselected(TabLayout.Tab tab) {}
+ @Override
+ public void onTabUnselected(TabLayout.Tab tab) {
+ }
- @Override
- public void onTabReselected(TabLayout.Tab tab) {
- jumpToTop();
- }
- };
- layout.addOnTabSelectedListener(onTabSelectedListener);
+ @Override
+ public void onTabReselected(TabLayout.Tab tab) {
+ jumpToTop();
+ }
+ };
+ layout.addOnTabSelectedListener(onTabSelectedListener);
+ }
return rootView;
}
@Override
public void onDestroyView() {
- TabLayout tabLayout = (TabLayout) getActivity().findViewById(R.id.tab_layout);
- tabLayout.removeOnTabSelectedListener(onTabSelectedListener);
+ if (jumpToTopAllowed()) {
+ TabLayout tabLayout = (TabLayout) getActivity().findViewById(R.id.tab_layout);
+ tabLayout.removeOnTabSelectedListener(onTabSelectedListener);
+ }
super.onDestroyView();
}
private void fetchAccounts(final String fromId) {
- int endpointId;
+ String endpoint;
switch (type) {
default:
case FOLLOWS: {
- endpointId = R.string.endpoint_following;
+ endpoint = String.format(getString(R.string.endpoint_following), accountId);
break;
}
case FOLLOWERS: {
- endpointId = R.string.endpoint_followers;
+ endpoint = String.format(getString(R.string.endpoint_followers), accountId);
+ break;
+ }
+ case BLOCKS: {
+ endpoint = getString(R.string.endpoint_blocks);
break;
}
}
- String endpoint = String.format(getString(endpointId), accountId);
String url = "https://" + domain + endpoint;
if (fromId != null) {
url += "?max_id=" + fromId;
@@ -172,7 +189,7 @@ public class AccountFragment extends Fragment implements AccountActionListener,
onFetchAccountsFailure(e);
return;
}
- onFetchAccountsSuccess(accounts, fromId != null);
+ onFetchAccountsSuccess(accounts, fromId);
}
},
new Response.ErrorListener() {
@@ -195,16 +212,25 @@ public class AccountFragment extends Fragment implements AccountActionListener,
fetchAccounts(null);
}
- private void onFetchAccountsSuccess(List accounts, boolean added) {
- if (added) {
- adapter.addItems(accounts);
- } else {
- adapter.update(accounts);
+ private static boolean findAccount(List accounts, String id) {
+ for (Account account : accounts) {
+ if (account.id.equals(id)) {
+ return true;
+ }
}
- if (accounts.size() >= EXPECTED_ACCOUNTS_FETCHED) {
- setFetchTimelineState(FooterViewHolder.State.LOADING);
+ return false;
+ }
+
+ private void onFetchAccountsSuccess(List accounts, String fromId) {
+ if (fromId != null) {
+ if (accounts.size() > 0 && !findAccount(accounts, fromId)) {
+ setFetchTimelineState(FooterViewHolder.State.LOADING);
+ adapter.addItems(accounts);
+ } else {
+ setFetchTimelineState(FooterViewHolder.State.END_OF_TIMELINE);
+ }
} else {
- setFetchTimelineState(FooterViewHolder.State.END_OF_TIMELINE);
+ adapter.update(accounts);
}
}
@@ -214,6 +240,9 @@ public class AccountFragment extends Fragment implements AccountActionListener,
}
private void setFetchTimelineState(FooterViewHolder.State state) {
+ // Set the adapter to set its state when it's bound, if the current Footer is offscreen.
+ adapter.setFooterState(state);
+ // Check if it's onscreen, and update it directly if it is.
RecyclerView.ViewHolder viewHolder =
recyclerView.findViewHolderForAdapterPosition(adapter.getItemCount() - 1);
if (viewHolder != null) {
@@ -237,6 +266,10 @@ public class AccountFragment extends Fragment implements AccountActionListener,
startActivity(intent);
}
+ private boolean jumpToTopAllowed() {
+ return type != Type.BLOCKS;
+ }
+
private void jumpToTop() {
layoutManager.scrollToPositionWithOffset(0, 0);
scrollListener.reset();
diff --git a/app/src/main/java/com/keylesspalace/tusky/BlocksActivity.java b/app/src/main/java/com/keylesspalace/tusky/BlocksActivity.java
new file mode 100644
index 00000000..99e39193
--- /dev/null
+++ b/app/src/main/java/com/keylesspalace/tusky/BlocksActivity.java
@@ -0,0 +1,43 @@
+/* Copyright 2017 Andrew Dawson
+ *
+ * This file is part of Tusky.
+ *
+ * Tusky is free software: you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
+ * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with Tusky. If not, see
+ * . */
+
+package com.keylesspalace.tusky;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v7.app.ActionBar;
+import android.support.v7.widget.Toolbar;
+
+public class BlocksActivity extends BaseActivity {
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_blocks);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ ActionBar bar = getSupportActionBar();
+ if (bar != null) {
+ bar.setTitle(getString(R.string.title_blocks));
+ }
+
+ FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
+ Fragment fragment = AccountFragment.newInstance(AccountFragment.Type.BLOCKS);
+ fragmentTransaction.add(R.id.fragment_container, fragment);
+ fragmentTransaction.commit();
+ }
+}
diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java
index 7a2ee6a5..afab447a 100644
--- a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java
+++ b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java
@@ -175,22 +175,27 @@ public class MainActivity extends BaseActivity {
startActivity(intent);
return true;
}
- case R.id.action_profile: {
+ case R.id.action_view_profile: {
Intent intent = new Intent(this, AccountActivity.class);
intent.putExtra("id", loggedInAccountId);
startActivity(intent);
return true;
}
- case R.id.action_preferences: {
+ case R.id.action_view_preferences: {
Intent intent = new Intent(this, PreferencesActivity.class);
startActivity(intent);
return true;
}
- case R.id.action_favourites: {
+ case R.id.action_view_favourites: {
Intent intent = new Intent(this, FavouritesActivity.class);
startActivity(intent);
return true;
}
+ case R.id.action_view_blocks: {
+ Intent intent = new Intent(this, BlocksActivity.class);
+ startActivity(intent);
+ return true;
+ }
case R.id.action_logout: {
if (notificationServiceEnabled) {
alarmManager.cancel(serviceAlarmIntent);
diff --git a/app/src/main/java/com/keylesspalace/tusky/NotificationsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/NotificationsAdapter.java
index f8dcfa3d..ca306433 100644
--- a/app/src/main/java/com/keylesspalace/tusky/NotificationsAdapter.java
+++ b/app/src/main/java/com/keylesspalace/tusky/NotificationsAdapter.java
@@ -37,6 +37,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter implements Adapte
private List notifications;
private StatusActionListener statusListener;
private FooterActionListener footerListener;
+ private FooterViewHolder.State footerState;
public NotificationsAdapter(StatusActionListener statusListener,
FooterActionListener footerListener) {
@@ -44,6 +45,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter implements Adapte
notifications = new ArrayList<>();
this.statusListener = statusListener;
this.footerListener = footerListener;
+ footerState = FooterViewHolder.State.LOADING;
}
@Override
@@ -100,6 +102,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter implements Adapte
}
} else {
FooterViewHolder holder = (FooterViewHolder) viewHolder;
+ holder.setState(footerState);
holder.setupButton(footerListener);
holder.setRetryMessage(R.string.footer_retry_notifications);
holder.setEndOfTimelineMessage(R.string.footer_end_of_notifications);
@@ -170,6 +173,10 @@ public class NotificationsAdapter extends RecyclerView.Adapter implements Adapte
notifyItemChanged(position);
}
+ public void setFooterState(FooterViewHolder.State state) {
+ footerState = state;
+ }
+
public static class FollowViewHolder extends RecyclerView.ViewHolder {
private TextView message;
diff --git a/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java b/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java
index 552f6e9b..cd742582 100644
--- a/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java
@@ -20,7 +20,6 @@ import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.TabLayout;
-import android.support.v4.content.ContextCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
@@ -44,7 +43,6 @@ import java.util.Map;
public class NotificationsFragment extends SFragment implements
SwipeRefreshLayout.OnRefreshListener, StatusActionListener, FooterActionListener {
private static final String TAG = "Notifications"; // logging tag
- private static final int EXPECTED_NOTIFICATIONS_FETCHED = 10;
private SwipeRefreshLayout swipeRefreshLayout;
private RecyclerView recyclerView;
@@ -77,7 +75,8 @@ public class NotificationsFragment extends SFragment implements
recyclerView.setLayoutManager(layoutManager);
DividerItemDecoration divider = new DividerItemDecoration(
context, layoutManager.getOrientation());
- Drawable drawable = ContextCompat.getDrawable(context, R.drawable.status_divider_dark);
+ Drawable drawable = ThemeUtils.getDrawable(context, R.attr.status_divider_drawable,
+ R.drawable.status_divider_dark);
divider.setDrawable(drawable);
recyclerView.addItemDecoration(divider);
scrollListener = new EndlessOnScrollListener(layoutManager) {
@@ -140,7 +139,7 @@ public class NotificationsFragment extends SFragment implements
public void onResponse(JSONArray response) {
try {
List notifications = Notification.parse(response);
- onFetchNotificationsSuccess(notifications, fromId != null);
+ onFetchNotificationsSuccess(notifications, fromId);
} catch (JSONException e) {
onFetchNotificationsFailure(e);
}
@@ -165,16 +164,25 @@ public class NotificationsFragment extends SFragment implements
sendFetchNotificationsRequest(null);
}
- private void onFetchNotificationsSuccess(List notifications, boolean added) {
- if (added) {
- adapter.addItems(notifications);
- } else {
- adapter.update(notifications);
+ private static boolean findNotification(List notifications, String id) {
+ for (Notification notification : notifications) {
+ if (notification.getId().equals(id)) {
+ return true;
+ }
}
- if (notifications.size() >= EXPECTED_NOTIFICATIONS_FETCHED) {
- setFetchTimelineState(FooterViewHolder.State.LOADING);
+ return false;
+ }
+
+ private void onFetchNotificationsSuccess(List notifications, String fromId) {
+ if (fromId != null) {
+ if (notifications.size() > 0 && !findNotification(notifications, fromId)) {
+ setFetchTimelineState(FooterViewHolder.State.LOADING);
+ adapter.addItems(notifications);
+ } else {
+ setFetchTimelineState(FooterViewHolder.State.END_OF_TIMELINE);
+ }
} else {
- setFetchTimelineState(FooterViewHolder.State.END_OF_TIMELINE);
+ adapter.update(notifications);
}
swipeRefreshLayout.setRefreshing(false);
}
@@ -186,6 +194,7 @@ public class NotificationsFragment extends SFragment implements
}
private void setFetchTimelineState(FooterViewHolder.State state) {
+ adapter.setFooterState(state);
RecyclerView.ViewHolder viewHolder =
recyclerView.findViewHolderForAdapterPosition(adapter.getItemCount() - 1);
if (viewHolder != null) {
diff --git a/app/src/main/java/com/keylesspalace/tusky/TimelineAdapter.java b/app/src/main/java/com/keylesspalace/tusky/TimelineAdapter.java
index d14e9ec6..0fe360de 100644
--- a/app/src/main/java/com/keylesspalace/tusky/TimelineAdapter.java
+++ b/app/src/main/java/com/keylesspalace/tusky/TimelineAdapter.java
@@ -31,6 +31,7 @@ public class TimelineAdapter extends RecyclerView.Adapter implements AdapterItem
private List statuses;
private StatusActionListener statusListener;
private FooterActionListener footerListener;
+ private FooterViewHolder.State footerState;
public TimelineAdapter(StatusActionListener statusListener,
FooterActionListener footerListener) {
@@ -38,6 +39,7 @@ public class TimelineAdapter extends RecyclerView.Adapter implements AdapterItem
statuses = new ArrayList<>();
this.statusListener = statusListener;
this.footerListener = footerListener;
+ footerState = FooterViewHolder.State.LOADING;
}
@Override
@@ -65,6 +67,7 @@ public class TimelineAdapter extends RecyclerView.Adapter implements AdapterItem
holder.setupWithStatus(status, statusListener, position);
} else {
FooterViewHolder holder = (FooterViewHolder) viewHolder;
+ holder.setState(footerState);
holder.setupButton(footerListener);
holder.setRetryMessage(R.string.footer_retry_statuses);
holder.setEndOfTimelineMessage(R.string.footer_end_of_statuses);
@@ -121,4 +124,8 @@ public class TimelineAdapter extends RecyclerView.Adapter implements AdapterItem
}
return null;
}
+
+ public void setFooterState(FooterViewHolder.State state) {
+ footerState = state;
+ }
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/TimelineFragment.java b/app/src/main/java/com/keylesspalace/tusky/TimelineFragment.java
index 50a62c2d..0c2d6983 100644
--- a/app/src/main/java/com/keylesspalace/tusky/TimelineFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/TimelineFragment.java
@@ -19,7 +19,6 @@ import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
-import android.support.v4.content.ContextCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
@@ -43,7 +42,6 @@ import java.util.Map;
public class TimelineFragment extends SFragment implements
SwipeRefreshLayout.OnRefreshListener, StatusActionListener, FooterActionListener {
private static final String TAG = "Timeline"; // logging tag
- private static final int EXPECTED_STATUSES_FETCHED = 20;
public enum Kind {
HOME,
@@ -207,7 +205,7 @@ public class TimelineFragment extends SFragment implements
onFetchTimelineFailure(e);
}
if (statuses != null) {
- onFetchTimelineSuccess(statuses, fromId != null);
+ onFetchTimelineSuccess(statuses, fromId);
}
}
}, new Response.ErrorListener() {
@@ -230,16 +228,25 @@ public class TimelineFragment extends SFragment implements
sendFetchTimelineRequest(null);
}
- public void onFetchTimelineSuccess(List statuses, boolean added) {
- if (added) {
- adapter.addItems(statuses);
- } else {
- adapter.update(statuses);
+ private static boolean findStatus(List statuses, String id) {
+ for (Status status : statuses) {
+ if (status.getId().equals(id)) {
+ return true;
+ }
}
- if (statuses.size() >= EXPECTED_STATUSES_FETCHED) {
- setFetchTimelineState(FooterViewHolder.State.LOADING);
+ return false;
+ }
+
+ public void onFetchTimelineSuccess(List statuses, String fromId) {
+ if (fromId != null) {
+ if (statuses.size() > 0 && !findStatus(statuses, fromId)) {
+ setFetchTimelineState(FooterViewHolder.State.LOADING);
+ adapter.addItems(statuses);
+ } else {
+ setFetchTimelineState(FooterViewHolder.State.END_OF_TIMELINE);
+ }
} else {
- setFetchTimelineState(FooterViewHolder.State.END_OF_TIMELINE);
+ adapter.update(statuses);
}
swipeRefreshLayout.setRefreshing(false);
}
@@ -251,6 +258,7 @@ public class TimelineFragment extends SFragment implements
}
private void setFetchTimelineState(FooterViewHolder.State state) {
+ adapter.setFooterState(state);
RecyclerView.ViewHolder viewHolder =
recyclerView.findViewHolderForAdapterPosition(adapter.getItemCount() - 1);
if (viewHolder != null) {
diff --git a/app/src/main/res/layout/activity_blocks.xml b/app/src/main/res/layout/activity_blocks.xml
new file mode 100644
index 00000000..aa8a3730
--- /dev/null
+++ b/app/src/main/res/layout/activity_blocks.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/main_toolbar.xml b/app/src/main/res/menu/main_toolbar.xml
index 57ee72a2..8ed161a5 100644
--- a/app/src/main/res/menu/main_toolbar.xml
+++ b/app/src/main/res/menu/main_toolbar.xml
@@ -10,18 +10,23 @@
app:showAsAction="always" />
+
+
- Follows
Followers
Favourites
+ Blocked Users
\@%s
%s boosted
@@ -100,11 +101,12 @@
Cancel
Close
Back
- Profile
+ Profile
+ Preferences
+ Favourites
+ Blocked Users
Open In Web
- Preferences
Set
- Favourites
Toot!