Dbrant has uploaded a new change for review. (
https://gerrit.wikimedia.org/r/389872 )
Change subject: New: Make Feed content completely configurable.
......................................................................
New: Make Feed content completely configurable.
Bug: T141397
Change-Id: Icace97fe0af8490983555bcd0661ec237d051af7
---
M app/src/main/AndroidManifest.xml
M app/src/main/java/org/wikipedia/Constants.java
A app/src/main/java/org/wikipedia/feed/FeedContentType.java
M app/src/main/java/org/wikipedia/feed/FeedCoordinator.java
M app/src/main/java/org/wikipedia/feed/FeedCoordinatorBase.java
M app/src/main/java/org/wikipedia/feed/FeedFragment.java
A app/src/main/java/org/wikipedia/feed/configure/ConfigureActivity.java
A app/src/main/java/org/wikipedia/feed/configure/ConfigureFragment.java
A app/src/main/java/org/wikipedia/feed/configure/ConfigureItemView.java
M app/src/main/java/org/wikipedia/settings/Prefs.java
M app/src/main/java/org/wikipedia/views/ExploreOverflowView.java
A app/src/main/res/drawable/ic_drag_handle_black_24dp.xml
A app/src/main/res/layout/fragment_feed_configure.xml
A app/src/main/res/layout/item_feed_content_type.xml
M app/src/main/res/layout/view_explore_overflow.xml
A app/src/main/res/menu/menu_feed_configure.xml
M app/src/main/res/values-qq/strings.xml
M app/src/main/res/values/preference_keys.xml
M app/src/main/res/values/strings.xml
19 files changed, 628 insertions(+), 26 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/apps/android/wikipedia
refs/changes/72/389872/1
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 02b4856..8448bf2 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -236,6 +236,10 @@
android:label="@string/on_this_day"
android:theme="@style/AppTheme.ActionBar"/>
+ <activity android:name=".feed.configure.ConfigureActivity"
+ android:label="@string/feed_configure_activity_title"
+ android:theme="@style/AppTheme.ActionBar"/>
+
<provider
android:authorities="${applicationId}"
android:name=".database.AppContentProvider"
diff --git a/app/src/main/java/org/wikipedia/Constants.java
b/app/src/main/java/org/wikipedia/Constants.java
index 8d42d6e..2e7cef1 100644
--- a/app/src/main/java/org/wikipedia/Constants.java
+++ b/app/src/main/java/org/wikipedia/Constants.java
@@ -24,6 +24,7 @@
public static final int ACTIVITY_REQUEST_DESCRIPTION_EDIT = 55;
public static final int ACTIVITY_REQUEST_DESCRIPTION_EDIT_TUTORIAL = 56;
public static final int ACTIVITY_REQUEST_OFFLINE_TUTORIAL = 57;
+ public static final int ACTIVITY_REQUEST_FEED_CONFIGURE = 58;
public static final String INTENT_RETURN_TO_MAIN = "returnToMain";
public static final String INTENT_SEARCH_FROM_WIDGET = "searchFromWidget";
diff --git a/app/src/main/java/org/wikipedia/feed/FeedContentType.java
b/app/src/main/java/org/wikipedia/feed/FeedContentType.java
new file mode 100644
index 0000000..0731980
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/feed/FeedContentType.java
@@ -0,0 +1,153 @@
+package org.wikipedia.feed;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
+
+import org.wikipedia.R;
+import org.wikipedia.feed.aggregated.AggregatedFeedContentClient;
+import org.wikipedia.feed.becauseyouread.BecauseYouReadClient;
+import org.wikipedia.feed.continuereading.ContinueReadingClient;
+import org.wikipedia.feed.dataclient.FeedClient;
+import org.wikipedia.feed.mainpage.MainPageClient;
+import org.wikipedia.feed.onthisday.OnThisDayClient;
+import org.wikipedia.feed.random.RandomClient;
+import org.wikipedia.model.EnumCode;
+import org.wikipedia.model.EnumCodeMap;
+import org.wikipedia.settings.Prefs;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.wikipedia.util.ReleaseUtil.isPreBetaRelease;
+
+public enum FeedContentType implements EnumCode {
+ NEWS(0, R.string.view_card_news_title) {
+ @Nullable
+ @Override
+ public FeedClient newClient(AggregatedFeedContentClient
aggregatedClient, int age, boolean isOnline) {
+ return isEnabled() && age == 0 && isOnline ? new
AggregatedFeedContentClient.InTheNews(aggregatedClient) : null;
+ }
+ },
+ FEATURED_ARTICLE(1, R.string.view_featured_article_card_title) {
+ @Nullable
+ @Override
+ public FeedClient newClient(AggregatedFeedContentClient
aggregatedClient, int age, boolean isOnline) {
+ return isEnabled() && isOnline ? new
AggregatedFeedContentClient.FeaturedArticle(aggregatedClient) : null;
+ }
+ },
+ TRENDING_ARTICLES(2, R.string.most_read_list_card_title) {
+ @Nullable
+ @Override
+ public FeedClient newClient(AggregatedFeedContentClient
aggregatedClient, int age, boolean isOnline) {
+ return isEnabled() && isOnline ? new
AggregatedFeedContentClient.TrendingArticles(aggregatedClient) : null;
+ }
+ },
+ FEATURED_IMAGE(3, R.string.view_featured_image_card_title) {
+ @Nullable
+ @Override
+ public FeedClient newClient(AggregatedFeedContentClient
aggregatedClient, int age, boolean isOnline) {
+ return isEnabled() && isOnline ? new
AggregatedFeedContentClient.FeaturedImage(aggregatedClient) : null;
+ }
+ },
+ ON_THIS_DAY(4, R.string.on_this_day_card_title) {
+ @Nullable
+ @Override
+ public FeedClient newClient(AggregatedFeedContentClient
aggregatedClient, int age, boolean isOnline) {
+ return isEnabled() && isOnline && isPreBetaRelease() ? new
OnThisDayClient() : null;
+ }
+ },
+ CONTINUE_READING(5, R.string.view_continue_reading_card_title) {
+ @Nullable
+ @Override
+ public FeedClient newClient(AggregatedFeedContentClient
aggregatedClient, int age, boolean isOnline) {
+ return isEnabled() ? new ContinueReadingClient() : null;
+ }
+ },
+ BECAUSE_YOU_READ(6, R.string.view_because_you_read_card_title) {
+ @Nullable
+ @Override
+ public FeedClient newClient(AggregatedFeedContentClient
aggregatedClient, int age, boolean isOnline) {
+ return isEnabled() && isOnline ? new BecauseYouReadClient() : null;
+ }
+ },
+ MAIN_PAGE(7, R.string.view_main_page_card_title) {
+ @Nullable
+ @Override
+ public FeedClient newClient(AggregatedFeedContentClient
aggregatedClient, int age, boolean isOnline) {
+ return isEnabled() && age == 0 ? new MainPageClient() : null;
+ }
+ },
+ RANDOM(8, R.string.view_random_card_title) {
+ @Nullable
+ @Override
+ public FeedClient newClient(AggregatedFeedContentClient
aggregatedClient, int age, boolean isOnline) {
+ return isEnabled() && age % 2 == 0 ? new RandomClient() : null;
+ }
+ };
+
+ private static final EnumCodeMap<FeedContentType> MAP
+ = new EnumCodeMap<>(FeedContentType.class);
+ private final int code;
+ @StringRes private final int titleId;
+ private int order;
+ private boolean enabled = true;
+
+ @NonNull public static FeedContentType of(int code) {
+ return MAP.get(code);
+ }
+
+ @Nullable
+ public abstract FeedClient newClient(AggregatedFeedContentClient
aggregatedClient,
+ int age, boolean isOnline);
+
+ @Override public int code() {
+ return code;
+ }
+
+ public int titleId() {
+ return titleId;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public Integer getOrder() {
+ return order;
+ }
+
+ public void setOrder(int order) {
+ this.order = order;
+ }
+
+ FeedContentType(int code, @StringRes int titleId) {
+ this.code = code;
+ this.order = code;
+ this.titleId = titleId;
+ }
+
+ public static void saveState() {
+ List<Boolean> enabledList = new ArrayList<>();
+ List<Integer> orderList = new ArrayList<>();
+ for (int i = 0; i < FeedContentType.values().length; i++) {
+ enabledList.add(FeedContentType.values()[i].isEnabled());
+ orderList.add(FeedContentType.values()[i].getOrder());
+ }
+ Prefs.setFeedCardsEnabled(enabledList);
+ Prefs.setFeedCardsOrder(orderList);
+ }
+
+ public static void restoreState() {
+ List<Boolean> enabledList = Prefs.getFeedCardsEnabled();
+ List<Integer> orderList = Prefs.getFeedCardsOrder();
+ for (int i = 0; i < FeedContentType.values().length; i++) {
+ FeedContentType.values()[i].setEnabled(i < enabledList.size() ?
enabledList.get(i) : true);
+ FeedContentType.values()[i].setOrder(i < orderList.size() ?
orderList.get(i) : i);
+ }
+ }
+}
diff --git a/app/src/main/java/org/wikipedia/feed/FeedCoordinator.java
b/app/src/main/java/org/wikipedia/feed/FeedCoordinator.java
index 5af0e97..1d4a040 100644
--- a/app/src/main/java/org/wikipedia/feed/FeedCoordinator.java
+++ b/app/src/main/java/org/wikipedia/feed/FeedCoordinator.java
@@ -5,24 +5,23 @@
import org.wikipedia.feed.aggregated.AggregatedFeedContentClient;
import org.wikipedia.feed.announcement.AnnouncementClient;
-import org.wikipedia.feed.becauseyouread.BecauseYouReadClient;
-import org.wikipedia.feed.continuereading.ContinueReadingClient;
-import org.wikipedia.feed.mainpage.MainPageClient;
import org.wikipedia.feed.offline.OfflineCompilationClient;
import org.wikipedia.feed.onboarding.OnboardingClient;
-import org.wikipedia.feed.onthisday.OnThisDayClient;
-import org.wikipedia.feed.random.RandomClient;
import org.wikipedia.feed.searchbar.SearchClient;
import org.wikipedia.offline.OfflineManager;
import org.wikipedia.util.DeviceUtil;
-import static org.wikipedia.util.ReleaseUtil.isPreBetaRelease;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
-class FeedCoordinator extends FeedCoordinatorBase {
+public class FeedCoordinator extends FeedCoordinatorBase {
@NonNull private AggregatedFeedContentClient aggregatedClient = new
AggregatedFeedContentClient();
FeedCoordinator(@NonNull Context context) {
super(context);
+ FeedContentType.restoreState();
}
@Override
@@ -33,14 +32,14 @@
conditionallyAddPendingClient(new OfflineCompilationClient(), age == 0
&& !online && OfflineManager.hasCompilation());
conditionallyAddPendingClient(new OnboardingClient(), age == 0);
conditionallyAddPendingClient(new AnnouncementClient(), age == 0 &&
online);
- conditionallyAddPendingClient(new
AggregatedFeedContentClient.InTheNews(aggregatedClient), online);
- conditionallyAddPendingClient(new
AggregatedFeedContentClient.FeaturedArticle(aggregatedClient), online);
- conditionallyAddPendingClient(new
AggregatedFeedContentClient.TrendingArticles(aggregatedClient), online);
- conditionallyAddPendingClient(new
AggregatedFeedContentClient.FeaturedImage(aggregatedClient), online);
- addPendingClient(new ContinueReadingClient());
- conditionallyAddPendingClient(new OnThisDayClient(), online &&
isPreBetaRelease());
- conditionallyAddPendingClient(new MainPageClient(), age == 0);
- conditionallyAddPendingClient(new BecauseYouReadClient(), online);
- conditionallyAddPendingClient(new RandomClient(), age == 0);
+
+ List<FeedContentType> orderedContentTypes = new ArrayList<>();
+ orderedContentTypes.addAll(Arrays.asList(FeedContentType.values()));
+ Collections.sort(orderedContentTypes, (FeedContentType a,
FeedContentType b)
+ -> a.getOrder().compareTo(b.getOrder()));
+
+ for (FeedContentType contentType : orderedContentTypes) {
+ addPendingClient(contentType.newClient(aggregatedClient, age,
online));
+ }
}
}
diff --git a/app/src/main/java/org/wikipedia/feed/FeedCoordinatorBase.java
b/app/src/main/java/org/wikipedia/feed/FeedCoordinatorBase.java
index f5fc669..11a1894 100644
--- a/app/src/main/java/org/wikipedia/feed/FeedCoordinatorBase.java
+++ b/app/src/main/java/org/wikipedia/feed/FeedCoordinatorBase.java
@@ -69,15 +69,15 @@
cards.clear();
}
+ public void incrementAge() {
+ currentAge++;
+ }
+
public void more(@NonNull WikiSite wiki) {
this.wiki = wiki;
if (cards.size() == 0) {
insertCard(progressCard, 0);
- }
-
- if (cards.size() > 1) {
- currentAge++;
}
buildScript(currentAge);
@@ -114,12 +114,14 @@
protected abstract void buildScript(int age);
- void addPendingClient(FeedClient client) {
- pendingClients.add(client);
+ void addPendingClient(@Nullable FeedClient client) {
+ if (client != null) {
+ pendingClients.add(client);
+ }
}
- void conditionallyAddPendingClient(FeedClient client, boolean condition) {
- if (condition) {
+ void conditionallyAddPendingClient(@Nullable FeedClient client, boolean
condition) {
+ if (condition && client != null) {
pendingClients.add(client);
}
}
diff --git a/app/src/main/java/org/wikipedia/feed/FeedFragment.java
b/app/src/main/java/org/wikipedia/feed/FeedFragment.java
index afacfa9..8d4e294 100644
--- a/app/src/main/java/org/wikipedia/feed/FeedFragment.java
+++ b/app/src/main/java/org/wikipedia/feed/FeedFragment.java
@@ -19,10 +19,12 @@
import org.wikipedia.BackPressedHandler;
import org.wikipedia.BuildConfig;
+import org.wikipedia.Constants;
import org.wikipedia.R;
import org.wikipedia.WikipediaApp;
import org.wikipedia.activity.FragmentUtil;
import org.wikipedia.analytics.FeedFunnel;
+import org.wikipedia.feed.configure.ConfigureActivity;
import org.wikipedia.feed.featured.FeaturedArticleCard;
import org.wikipedia.feed.image.FeaturedImage;
import org.wikipedia.feed.image.FeaturedImageCard;
@@ -51,6 +53,7 @@
import butterknife.Unbinder;
import static android.app.Activity.RESULT_OK;
+import static org.wikipedia.Constants.ACTIVITY_REQUEST_FEED_CONFIGURE;
import static org.wikipedia.Constants.ACTIVITY_REQUEST_OFFLINE_TUTORIAL;
public class FeedFragment extends Fragment implements BackPressedHandler {
@@ -196,6 +199,9 @@
Prefs.setOfflineTutorialEnabled(false);
refresh();
feedCallback.onViewCompilations();
+ } else if (requestCode == ACTIVITY_REQUEST_FEED_CONFIGURE
+ && resultCode ==
ConfigureActivity.CONFIGURATION_CHANGED_RESULT) {
+ refresh();
}
}
@@ -311,7 +317,12 @@
@Override
public void onRequestMore() {
funnel.requestMore(coordinator.getAge());
- coordinator.more(app.getWikiSite());
+ feedView.post(() -> {
+ if (isAdded()) {
+ coordinator.incrementAge();
+ coordinator.more(app.getWikiSite());
+ }
+ });
}
@Override
@@ -530,6 +541,12 @@
}
@Override
+ public void configureCardsClick() {
+ startActivityForResult(ConfigureActivity.newIntent(getActivity()),
+ Constants.ACTIVITY_REQUEST_FEED_CONFIGURE);
+ }
+
+ @Override
public void logoutClick() {
WikipediaApp.getInstance().logOut();
FeedbackUtil.showMessage(FeedFragment.this,
R.string.toast_logout_complete);
diff --git
a/app/src/main/java/org/wikipedia/feed/configure/ConfigureActivity.java
b/app/src/main/java/org/wikipedia/feed/configure/ConfigureActivity.java
new file mode 100644
index 0000000..b7341ea
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/feed/configure/ConfigureActivity.java
@@ -0,0 +1,20 @@
+package org.wikipedia.feed.configure;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.annotation.NonNull;
+
+import org.wikipedia.activity.SingleFragmentActivity;
+
+public class ConfigureActivity extends
SingleFragmentActivity<ConfigureFragment> {
+ public static final int CONFIGURATION_CHANGED_RESULT = 1;
+
+ public static Intent newIntent(@NonNull Context context) {
+ return new Intent(context, ConfigureActivity.class);
+ }
+
+ @Override
+ protected ConfigureFragment createFragment() {
+ return ConfigureFragment.newInstance();
+ }
+}
diff --git
a/app/src/main/java/org/wikipedia/feed/configure/ConfigureFragment.java
b/app/src/main/java/org/wikipedia/feed/configure/ConfigureFragment.java
new file mode 100644
index 0000000..340021e
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/feed/configure/ConfigureFragment.java
@@ -0,0 +1,210 @@
+package org.wikipedia.feed.configure;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.wikipedia.R;
+import org.wikipedia.feed.FeedContentType;
+import org.wikipedia.settings.Prefs;
+import org.wikipedia.views.DefaultViewHolder;
+import org.wikipedia.views.HeaderMarginItemDecoration;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.Unbinder;
+
+public class ConfigureFragment extends Fragment implements
ConfigureItemView.Callback {
+ @BindView(R.id.content_types_recycler) RecyclerView recyclerView;
+ private Unbinder unbinder;
+ private ItemTouchHelper itemTouchHelper;
+ private List<FeedContentType> orderedContentTypes = new ArrayList<>();
+
+ @NonNull public static ConfigureFragment newInstance() {
+ return new ConfigureFragment();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_feed_configure,
container, false);
+ unbinder = ButterKnife.bind(this, view);
+
+ prepareContentTypeList();
+ setupRecyclerView();
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ FeedContentType.saveState();
+ }
+
+ @Override
+ public void onDestroyView() {
+ unbinder.unbind();
+ unbinder = null;
+ super.onDestroyView();
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ inflater.inflate(R.menu.menu_feed_configure, menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_feed_configure_reset:
+ Prefs.resetFeedCustomizations();
+ FeedContentType.restoreState();
+ prepareContentTypeList();
+
getActivity().setResult(ConfigureActivity.CONFIGURATION_CHANGED_RESULT);
+ recyclerView.getAdapter().notifyDataSetChanged();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void prepareContentTypeList() {
+ orderedContentTypes.clear();
+ orderedContentTypes.addAll(Arrays.asList(FeedContentType.values()));
+ Collections.sort(orderedContentTypes, (FeedContentType a,
FeedContentType b)
+ -> a.getOrder().compareTo(b.getOrder()));
+ }
+
+ private void setupRecyclerView() {
+ recyclerView.setHasFixedSize(true);
+ ConfigureItemAdapter adapter = new ConfigureItemAdapter();
+ recyclerView.setAdapter(adapter);
+ recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+
+ recyclerView.addItemDecoration(new
HeaderMarginItemDecoration(getContext(),
+ R.dimen.view_feed_padding_top,
+ R.dimen.view_feed_padding_top));
+
+ itemTouchHelper = new ItemTouchHelper(new
RearrangeableItemTouchHelperCallback(adapter));
+ itemTouchHelper.attachToRecyclerView(recyclerView);
+ }
+
+ @Override
+ public void onCheckedChanged(FeedContentType contentType, boolean checked)
{
+
getActivity().setResult(ConfigureActivity.CONFIGURATION_CHANGED_RESULT);
+ contentType.setEnabled(checked);
+ }
+
+ private void updateItemOrder() {
+
getActivity().setResult(ConfigureActivity.CONFIGURATION_CHANGED_RESULT);
+ for (int i = 0; i < orderedContentTypes.size(); i++) {
+ orderedContentTypes.get(i).setOrder(i);
+ }
+ }
+
+ private class ConfigureItemHolder extends
DefaultViewHolder<ConfigureItemView> {
+ ConfigureItemHolder(ConfigureItemView itemView) {
+ super(itemView);
+ }
+
+ void bindItem(FeedContentType contentType) {
+ getView().setContents(contentType);
+ }
+ }
+
+ private final class ConfigureItemAdapter extends
RecyclerView.Adapter<ConfigureItemHolder> {
+ @Override
+ public int getItemCount() {
+ return orderedContentTypes.size();
+ }
+
+ @Override
+ public ConfigureItemHolder onCreateViewHolder(ViewGroup parent, int
type) {
+ return new ConfigureItemHolder(new
ConfigureItemView(getContext()));
+ }
+
+ @Override
+ public void onBindViewHolder(ConfigureItemHolder holder, int pos) {
+ holder.bindItem(orderedContentTypes.get(pos));
+ }
+
+ @Override public void onViewAttachedToWindow(ConfigureItemHolder
holder) {
+ super.onViewAttachedToWindow(holder);
+ holder.getView().setDragHandleTouchListener(new
View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ itemTouchHelper.startDrag(holder);
+ }
+ return false;
+ }
+ });
+ holder.getView().setCallback(ConfigureFragment.this);
+ }
+
+ @Override public void onViewDetachedFromWindow(ConfigureItemHolder
holder) {
+ holder.getView().setCallback(null);
+ holder.getView().setDragHandleTouchListener(null);
+ super.onViewDetachedFromWindow(holder);
+ }
+
+ void onMoveItem(int oldPosition, int newPosition) {
+ Collections.swap(orderedContentTypes, oldPosition, newPosition);
+ updateItemOrder();
+ notifyItemMoved(oldPosition, newPosition);
+ }
+ }
+
+ private final class RearrangeableItemTouchHelperCallback extends
ItemTouchHelper.Callback {
+ private final ConfigureItemAdapter adapter;
+
+ RearrangeableItemTouchHelperCallback(ConfigureItemAdapter adapter) {
+ this.adapter = adapter;
+ }
+
+ @Override
+ public boolean isLongPressDragEnabled() {
+ return true;
+ }
+
+ @Override
+ public boolean isItemViewSwipeEnabled() {
+ return false;
+ }
+
+ @Override
+ public void onSwiped(RecyclerView.ViewHolder viewHolder, int
direction) {
+ }
+
+ @Override
+ public int getMovementFlags(RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder) {
+ return makeMovementFlags(ItemTouchHelper.UP |
ItemTouchHelper.DOWN, 0);
+ }
+
+ @Override
+ public boolean onMove(RecyclerView recyclerView,
RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
+ adapter.onMoveItem(source.getAdapterPosition(),
target.getAdapterPosition());
+ return true;
+ }
+ }
+}
diff --git
a/app/src/main/java/org/wikipedia/feed/configure/ConfigureItemView.java
b/app/src/main/java/org/wikipedia/feed/configure/ConfigureItemView.java
new file mode 100644
index 0000000..4e6bfe4
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/feed/configure/ConfigureItemView.java
@@ -0,0 +1,69 @@
+package org.wikipedia.feed.configure;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.FrameLayout;
+
+import org.wikipedia.R;
+import org.wikipedia.feed.FeedContentType;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.OnCheckedChanged;
+
+public class ConfigureItemView extends FrameLayout {
+ public interface Callback {
+ void onCheckedChanged(FeedContentType contentType, boolean checked);
+ }
+
+ @BindView(R.id.feed_content_type_checkbox) CheckBox titleView;
+ @BindView(R.id.feed_content_type_drag_handle) View dragHandleView;
+ @Nullable private Callback callback;
+ private FeedContentType contentType;
+
+ public ConfigureItemView(Context context) {
+ super(context);
+ init();
+ }
+
+ public ConfigureItemView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public ConfigureItemView(Context context, AttributeSet attrs, int
defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ public void setCallback(@Nullable Callback callback) {
+ this.callback = callback;
+ }
+
+ public void setContents(FeedContentType contentType) {
+ this.contentType = contentType;
+ titleView.setText(contentType.titleId());
+ titleView.setChecked(contentType.isEnabled());
+ }
+
+ public void setDragHandleTouchListener(OnTouchListener listener) {
+ dragHandleView.setOnTouchListener(listener);
+ }
+
+ private void init() {
+ inflate(getContext(), R.layout.item_feed_content_type, this);
+ ButterKnife.bind(this);
+ setLayoutParams(new
ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ }
+
+ @OnCheckedChanged(R.id.feed_content_type_checkbox) void
onCheckedChanged(boolean checked) {
+ if (callback != null) {
+ callback.onCheckedChanged(contentType, checked);
+ }
+ }
+}
diff --git a/app/src/main/java/org/wikipedia/settings/Prefs.java
b/app/src/main/java/org/wikipedia/settings/Prefs.java
index bba8353..79fe9ed 100644
--- a/app/src/main/java/org/wikipedia/settings/Prefs.java
+++ b/app/src/main/java/org/wikipedia/settings/Prefs.java
@@ -585,5 +585,39 @@
return getBoolean(R.string.preference_key_enable_offline_library,
false);
}
+ @NonNull public static List<Boolean> getFeedCardsEnabled() {
+ if (!contains(R.string.preference_key_feed_cards_enabled)) {
+ return Collections.emptyList();
+ }
+ //noinspection unchecked
+ List<Boolean> enabledList = GsonUnmarshaller.unmarshal(new
TypeToken<ArrayList<Boolean>>(){},
+ getString(R.string.preference_key_feed_cards_enabled, null));
+ return enabledList != null ? enabledList : Collections.emptyList();
+ }
+
+ public static void setFeedCardsEnabled(@NonNull List<Boolean> enabledList)
{
+ setString(R.string.preference_key_feed_cards_enabled,
GsonMarshaller.marshal(enabledList));
+ }
+
+ @NonNull public static List<Integer> getFeedCardsOrder() {
+ if (!contains(R.string.preference_key_feed_cards_order)) {
+ return Collections.emptyList();
+ }
+ //noinspection unchecked
+ List<Integer> orderList = GsonUnmarshaller.unmarshal(new
TypeToken<ArrayList<Integer>>(){},
+ getString(R.string.preference_key_feed_cards_order, null));
+ return orderList != null ? orderList : Collections.emptyList();
+ }
+
+ public static void setFeedCardsOrder(@NonNull List<Integer> orderList) {
+ setString(R.string.preference_key_feed_cards_order,
GsonMarshaller.marshal(orderList));
+ }
+
+ public static void resetFeedCustomizations() {
+ remove(R.string.preference_key_feed_hidden_cards);
+ remove(R.string.preference_key_feed_cards_enabled);
+ remove(R.string.preference_key_feed_cards_order);
+ }
+
private Prefs() { }
}
diff --git a/app/src/main/java/org/wikipedia/views/ExploreOverflowView.java
b/app/src/main/java/org/wikipedia/views/ExploreOverflowView.java
index 52ad376..6c80220 100644
--- a/app/src/main/java/org/wikipedia/views/ExploreOverflowView.java
+++ b/app/src/main/java/org/wikipedia/views/ExploreOverflowView.java
@@ -30,6 +30,7 @@
void compilationsClick();
void settingsClick();
void donateClick();
+ void configureCardsClick();
}
@BindView(R.id.explore_overflow_compilations) View compilationsView;
@@ -57,7 +58,7 @@
@OnClick({R.id.explore_overflow_settings, R.id.explore_overflow_donate,
R.id.explore_overflow_account_container,
R.id.explore_overflow_log_out,
- R.id.explore_overflow_compilations})
+ R.id.explore_overflow_compilations,
R.id.explore_overflow_configure_cards})
void onItemClick(View view) {
if (popupWindowHost != null) {
popupWindowHost.dismiss();
@@ -72,6 +73,9 @@
callback.loginClick();
}
break;
+ case R.id.explore_overflow_configure_cards:
+ callback.configureCardsClick();
+ break;
case R.id.explore_overflow_compilations:
callback.compilationsClick();
break;
diff --git a/app/src/main/res/drawable/ic_drag_handle_black_24dp.xml
b/app/src/main/res/drawable/ic_drag_handle_black_24dp.xml
new file mode 100644
index 0000000..68a7190
--- /dev/null
+++ b/app/src/main/res/drawable/ic_drag_handle_black_24dp.xml
@@ -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="#FF000000"
+ android:pathData="M20,9H4v2h16V9zM4,15h16v-2H4v2z"/>
+</vector>
diff --git a/app/src/main/res/layout/fragment_feed_configure.xml
b/app/src/main/res/layout/fragment_feed_configure.xml
new file mode 100644
index 0000000..42d7eeb
--- /dev/null
+++ b/app/src/main/res/layout/fragment_feed_configure.xml
@@ -0,0 +1,17 @@
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/content_types_recycler"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginLeft="@dimen/activity_horizontal_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:scrollbars="vertical" />
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/item_feed_content_type.xml
b/app/src/main/res/layout/item_feed_content_type.xml
new file mode 100644
index 0000000..dc62ce4
--- /dev/null
+++ b/app/src/main/res/layout/item_feed_content_type.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v7.widget.CardView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="64dp"
+ app:cardBackgroundColor="?attr/paper_color"
+ app:cardUseCompatPadding="true">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <CheckBox
+ android:id="@+id/feed_content_type_checkbox"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:drawablePadding="8dp"
+ style="@style/RtlAwareTextView"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?attr/secondary_text_color"
+ tools:text="Lorem ipsum"/>
+
+ <ImageView
+ android:id="@+id/feed_content_type_drag_handle"
+ android:layout_width="48dp"
+ android:layout_height="match_parent"
+ app:srcCompat="@drawable/ic_drag_handle_black_24dp"
+ android:scaleType="center"
+ android:tint="?attr/secondary_text_color"
+ android:contentDescription="@null"/>
+
+ </LinearLayout>
+
+</android.support.v7.widget.CardView>
diff --git a/app/src/main/res/layout/view_explore_overflow.xml
b/app/src/main/res/layout/view_explore_overflow.xml
index cf85ec7..bc89dca 100644
--- a/app/src/main/res/layout/view_explore_overflow.xml
+++ b/app/src/main/res/layout/view_explore_overflow.xml
@@ -46,6 +46,11 @@
android:background="?attr/paper_color">
<TextView
+ android:id="@+id/explore_overflow_configure_cards"
+ style="@style/OverflowMenuItem"
+ android:text="@string/feed_configure_menu_title"/>
+
+ <TextView
android:id="@+id/explore_overflow_compilations"
style="@style/OverflowMenuItem"
android:text="@string/offline_compilations_title"/>
diff --git a/app/src/main/res/menu/menu_feed_configure.xml
b/app/src/main/res/menu/menu_feed_configure.xml
new file mode 100644
index 0000000..93bcd19
--- /dev/null
+++ b/app/src/main/res/menu/menu_feed_configure.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:ignore="AlwaysShowAction">
+ <item android:id="@+id/menu_feed_configure_reset"
+ android:title="@string/feed_configure_menu_reset"
+ app:showAsAction="never"/>
+</menu>
diff --git a/app/src/main/res/values-qq/strings.xml
b/app/src/main/res/values-qq/strings.xml
index 755b5fc..d2df1dc 100644
--- a/app/src/main/res/values-qq/strings.xml
+++ b/app/src/main/res/values-qq/strings.xml
@@ -399,6 +399,9 @@
<string name="view_featured_article_card_title">Title shown in the Featured
Article card in the Explore feed\n{{Identical|Featured article}}</string>
<string name="view_featured_article_footer_save_button_label">Button label
for saving the current featured article to a reading list, if it is not already
saved.\n{{Identical|Save}}</string>
<string name="view_featured_article_footer_saved_button_label">Button label
for when the current featured article is already saved to a reading list, but
may now be removed from the list, or saved to a different
list.\n{{Identical|Saved}}</string>
+ <string name="feed_configure_menu_title">Menu label for showing the screen
where the user configures which cards are shown in the Explore feed.</string>
+ <string name="feed_configure_activity_title">Title shown at the top of the
screen where the user configures which cards are shown in the Explore
feed.</string>
+ <string name="feed_configure_menu_reset">Menu label for resetting the
configuration of the Explore feed cards to the original defaults.</string>
<string name="description_edit_text_hint">Hint text that is shown when the
description field is empty.</string>
<string name="description_edit_save">Hint for the button that is pressed for
saving the new description.\n{{Identical|Publish}}</string>
<string name="description_edit_add_description">Label that prompts the user
to add a description to an article that does not yet have one.</string>
diff --git a/app/src/main/res/values/preference_keys.xml
b/app/src/main/res/values/preference_keys.xml
index c238e01..9721fc0 100644
--- a/app/src/main/res/values/preference_keys.xml
+++ b/app/src/main/res/values/preference_keys.xml
@@ -65,4 +65,6 @@
<string
name="preference_key_suppress_notification_polling">suppressNotificationPolling</string>
<string
name="preference_key_prefer_offline_content">preferOfflineContent</string>
<string
name="preference_key_enable_offline_library">enableOfflineLibrary</string>
+ <string name="preference_key_feed_cards_order">feedCardsOrder</string>
+ <string name="preference_key_feed_cards_enabled">feedCardsEnabled</string>
</resources>
diff --git a/app/src/main/res/values/strings.xml
b/app/src/main/res/values/strings.xml
index 8fd8c41..fb85927 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -434,6 +434,9 @@
<string name="view_featured_article_card_title">Featured article</string>
<string name="view_featured_article_footer_save_button_label">Save</string>
<string
name="view_featured_article_footer_saved_button_label">Saved</string>
+ <string name="feed_configure_menu_title">Configure cards</string>
+ <string name="feed_configure_activity_title">Configure Explore
cards</string>
+ <string name="feed_configure_menu_reset">Reset to defaults</string>
<!-- /The Feed -->
<!-- Description editing -->
--
To view, visit https://gerrit.wikimedia.org/r/389872
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Icace97fe0af8490983555bcd0661ec237d051af7
Gerrit-PatchSet: 1
Gerrit-Project: apps/android/wikipedia
Gerrit-Branch: master
Gerrit-Owner: Dbrant <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits