diff --git a/OsmAnd/res/layout/wikivoyage_open_beta_card.xml b/OsmAnd/res/layout/wikivoyage_open_beta_card.xml index 5ba3eefd55..4f45dc23af 100644 --- a/OsmAnd/res/layout/wikivoyage_open_beta_card.xml +++ b/OsmAnd/res/layout/wikivoyage_open_beta_card.xml @@ -2,25 +2,30 @@ + android:layout_marginLeft="@dimen/text_margin_small" + android:layout_marginRight="@dimen/text_margin_small" + android:background="@drawable/travel_card_bg" + android:orientation="vertical"> + android:orientation="vertical" + android:background="@color/wikivoyage_open_beta_card_image_background"> + - \ No newline at end of file diff --git a/OsmAnd/res/layout/wikivoyage_start_editing_card.xml b/OsmAnd/res/layout/wikivoyage_start_editing_card.xml index 9874abc0ab..e2290fd173 100644 --- a/OsmAnd/res/layout/wikivoyage_start_editing_card.xml +++ b/OsmAnd/res/layout/wikivoyage_start_editing_card.xml @@ -2,11 +2,13 @@ 21dp 33dp 82dp + 216dp \ No newline at end of file diff --git a/OsmAnd/res/values/colors.xml b/OsmAnd/res/values/colors.xml index 08e38f2c6d..195697b21a 100644 --- a/OsmAnd/res/values/colors.xml +++ b/OsmAnd/res/values/colors.xml @@ -428,6 +428,7 @@ #212121 #cccccc #727272 - #339966 + #339966 + #008bf8 \ No newline at end of file diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index c054188440..19cdb51fc8 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -9,6 +9,7 @@ 3. All your modified/created strings are in the top of the file (to make easier find what\'s translated). PLEASE: Have a look at http://code.google.com/p/osmand/wiki/UIConsistency, it may really improve your and our work :-) Thx - Hardy --> + Popular destinations The free worldwide travel guide that anyone can edit. Travel is based on Wikivoyage. During open beta testing you have the opportunity to evaluate all the features for free. After the end of the beta period, Travel will be available to subscribers of OsmAnd Unlimited and the owners of OsmAnd+ You can edit any article on Wikivoyage, and we hope that you do. We need your knowledge, your experience, your talent, and your attention diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelDbHelper.java b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelDbHelper.java index d7b9ededc8..ddee382e71 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelDbHelper.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelDbHelper.java @@ -70,6 +70,9 @@ public class TravelDbHelper { ARTICLES_COL_AGGREGATED_PART_OF + " FROM " + ARTICLES_TABLE_NAME; + private static final String POPULAR_ARTICLES_TABLE_SELECT = "SELECT *" + + " FROM " + ARTICLES_TABLE_NAME + " ORDER BY RANDOM() LIMIT 100"; + private static final String SEARCH_TABLE_NAME = "wikivoyage_search"; private static final String SEARCH_COL_SEARCH_TERM = "search_term"; private static final String SEARCH_COL_CITY_ID = "city_id"; @@ -199,6 +202,24 @@ public class TravelDbHelper { return list; } + @NonNull + public List searchPopular() { + List res = new ArrayList<>(); + SQLiteConnection conn = openConnection(); + if (conn != null) { + TravelArticle travelArticle; + SQLiteCursor cursor = conn.rawQuery(POPULAR_ARTICLES_TABLE_SELECT, null); + if (cursor.moveToFirst()) { + do { + travelArticle = readArticle(cursor); + res.add(travelArticle); + } while (cursor.moveToNext()); + } + cursor.close(); + } + return res; + } + private void sortSearchResults(final String searchQuery, List list) { Collections.sort(list, new Comparator() { @Override diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/explore/ExploreRvAdapter.java b/OsmAnd/src/net/osmand/plus/wikivoyage/explore/ExploreRvAdapter.java new file mode 100644 index 0000000000..aff970e023 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/explore/ExploreRvAdapter.java @@ -0,0 +1,224 @@ +package net.osmand.plus.wikivoyage.explore; + +import android.graphics.drawable.Drawable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.Snackbar; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import com.squareup.picasso.Callback; +import com.squareup.picasso.Picasso; +import com.squareup.picasso.RequestCreator; + +import net.osmand.AndroidUtils; +import net.osmand.plus.IconsCache; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.OsmandSettings; +import net.osmand.plus.R; +import net.osmand.plus.widgets.tools.CropCircleTransformation; +import net.osmand.plus.widgets.tools.CropRectTransformation; +import net.osmand.plus.wikivoyage.WikivoyageUtils; +import net.osmand.plus.wikivoyage.data.TravelArticle; +import net.osmand.plus.wikivoyage.data.TravelLocalDataHelper; +import net.osmand.plus.wikivoyage.explore.travelcards.ArticleTravelCard; +import net.osmand.plus.wikivoyage.explore.travelcards.OpenBetaTravelCard; +import net.osmand.plus.wikivoyage.explore.travelcards.StartEditingTravelCard; + +import java.util.ArrayList; +import java.util.List; + +public class ExploreRvAdapter extends RecyclerView.Adapter { + + private static final int HEADER_TYPE = 3; + private static final int ITEM_TYPE = 4; + + private final OsmandSettings settings; + + private final List items = new ArrayList<>(); + + private ExploreRvAdapter.Listener listener; + OsmandApplication app; + private final Drawable readIcon; + private final Drawable deleteIcon; + + public void setListener(ExploreRvAdapter.Listener listener) { + this.listener = listener; + } + + ExploreRvAdapter(OsmandApplication app) { + this.app = app; + this.settings = app.getSettings(); + + int colorId = settings.isLightContent() + ? R.color.wikivoyage_active_light : R.color.wikivoyage_active_dark; + IconsCache ic = app.getIconsCache(); + readIcon = ic.getIcon(R.drawable.ic_action_read_article, colorId); + deleteIcon = ic.getIcon(R.drawable.ic_action_read_later_fill, colorId); + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + boolean header = viewType == HEADER_TYPE; + RecyclerView.ViewHolder holder = null; + View itemView = null; + int layoutId = 0; + if (viewType == OpenBetaTravelCard.TYPE) { + layoutId = R.layout.wikivoyage_open_beta_card; + itemView = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false); + return new OpenBetaTravelCard.OpenBetaTravelVH(itemView); + } + if (viewType == StartEditingTravelCard.TYPE) { + layoutId = R.layout.wikivoyage_start_editing_card; + itemView = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false); + return new StartEditingTravelCard.StartEditingTravelVH(itemView); + + } + if (viewType == ArticleTravelCard.TYPE) { + layoutId = ArticleTravelCard.USE_ALTERNATIVE_CARD ? R.layout.wikivoyage_article_card_alternative : R.layout.wikivoyage_article_card; + itemView = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false); + return new ArticleTravelCard.ArticleTravelVH(itemView); + } + if (viewType == HEADER_TYPE) { + layoutId = R.layout.wikivoyage_list_header; + itemView = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false); + return new HeaderVH(itemView); + } + return null; + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { + if (viewHolder instanceof HeaderVH) { + final HeaderVH holder = (HeaderVH) viewHolder; + holder.title.setText((String) getItem(position)); + holder.description.setText(String.valueOf(items.size() - 3)); + } else if (viewHolder instanceof ArticleTravelCard.ArticleTravelVH) { + if (getItem(position) instanceof ArticleTravelCard) { + ArticleTravelCard articleTravelCard = (ArticleTravelCard) getItem(position); + articleTravelCard.bindViewHolder(viewHolder); + } + } else if (viewHolder instanceof OpenBetaTravelCard.OpenBetaTravelVH) { + if (getItem(position) instanceof OpenBetaTravelCard) { + OpenBetaTravelCard openBetaTravelCard = (OpenBetaTravelCard) getItem(position); + openBetaTravelCard.bindViewHolder(viewHolder); + } + } else if (viewHolder instanceof StartEditingTravelCard.StartEditingTravelVH) { + if (getItem(position) instanceof StartEditingTravelCard) { + StartEditingTravelCard startEditingTravelCard = (StartEditingTravelCard) getItem(position); + startEditingTravelCard.bindViewHolder(viewHolder); + } + } + } + + @Override + public int getItemViewType(int position) { + if (getItem(position) instanceof String) { + return HEADER_TYPE; + } + if (getItem(position) instanceof OpenBetaTravelCard) { + return ((OpenBetaTravelCard) getItem(position)).getCardType(); + } + if (getItem(position) instanceof StartEditingTravelCard) { + return ((StartEditingTravelCard) getItem(position)).getCardType(); + } + if (getItem(position) instanceof ArticleTravelCard) { + return ((ArticleTravelCard) getItem(position)).getCardType(); + } + return -1; + } + + @Override + public int getItemCount() { + return items.size(); + } + + private Object getItem(int position) { + return items.get(position); + } + + @NonNull + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items.clear(); + this.items.addAll(items); + } + + static class HeaderVH extends RecyclerView.ViewHolder { + + final TextView title; + final TextView description; + + HeaderVH(View itemView) { + super(itemView); + title = (TextView) itemView.findViewById(R.id.title); + description = (TextView) itemView.findViewById(R.id.description); + } + } + + class ItemVH extends RecyclerView.ViewHolder { + + final TextView title; + final TextView content; + final TextView partOf; + final ImageView icon; + final TextView leftButton; + final TextView rightButton; + final View divider; + final View shadow; + + ItemVH(final View itemView) { + super(itemView); + title = (TextView) itemView.findViewById(R.id.title); + content = (TextView) itemView.findViewById(R.id.content); + partOf = (TextView) itemView.findViewById(R.id.part_of); + icon = (ImageView) itemView.findViewById(R.id.icon); + leftButton = (TextView) itemView.findViewById(R.id.left_button); + rightButton = (TextView) itemView.findViewById(R.id.right_button); + divider = itemView.findViewById(R.id.divider); + shadow = itemView.findViewById(R.id.shadow); + + View.OnClickListener readClickListener = new View.OnClickListener() { + @Override + public void onClick(View view) { + Object item = getItemByPosition(); + if (item != null && item instanceof TravelArticle) { + if (listener != null) { + listener.openArticle((TravelArticle) item); + } + } + } + }; + + itemView.setOnClickListener(readClickListener); + leftButton.setOnClickListener(readClickListener); + + rightButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + } + }); + } + + @Nullable + private Object getItemByPosition() { + int pos = getAdapterPosition(); + if (pos != RecyclerView.NO_POSITION) { + return getItem(pos); + } + return null; + } + } + + interface Listener { + void openArticle(TravelArticle article); + } +} diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/explore/ExploreTabFragment.java b/OsmAnd/src/net/osmand/plus/wikivoyage/explore/ExploreTabFragment.java index 0a584c615a..770ddede96 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/explore/ExploreTabFragment.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/explore/ExploreTabFragment.java @@ -3,41 +3,69 @@ package net.osmand.plus.wikivoyage.explore; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v4.app.FragmentManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.LinearLayout; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.base.BaseOsmAndFragment; -import net.osmand.plus.wikivoyage.explore.travelcards.BaseTravelCard; +import net.osmand.plus.wikivoyage.article.WikivoyageArticleDialogFragment; +import net.osmand.plus.wikivoyage.data.TravelArticle; +import net.osmand.plus.wikivoyage.explore.travelcards.ArticleTravelCard; import net.osmand.plus.wikivoyage.explore.travelcards.OpenBetaTravelCard; import net.osmand.plus.wikivoyage.explore.travelcards.StartEditingTravelCard; import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; public class ExploreTabFragment extends BaseOsmAndFragment { + boolean nightMode; + @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - OsmandApplication app = getMyApplication(); - boolean nightMode = !getSettings().isLightContent(); - + final OsmandApplication app = getMyApplication(); + nightMode = !getSettings().isLightContent(); final View mainView = inflater.inflate(R.layout.fragment_explore_tab, container, false); - ArrayList items = new ArrayList<>(); - OpenBetaTravelCard openBetaTravelCard = new OpenBetaTravelCard(app, nightMode, getFragmentManager()); - StartEditingTravelCard startEditingTravelCard = new StartEditingTravelCard(app, nightMode); - items.add(openBetaTravelCard); - items.add(startEditingTravelCard); + ExploreRvAdapter adapter = new ExploreRvAdapter(app); + adapter.setListener(new ExploreRvAdapter.Listener() { + @Override + public void openArticle(TravelArticle article) { + FragmentManager fm = getFragmentManager(); + if (fm != null) { + WikivoyageArticleDialogFragment.showInstance(app, fm, article.getCityId(), article.getLang()); + } + } + }); final RecyclerView rv = (RecyclerView) mainView.findViewById(R.id.recycler_view); rv.setLayoutManager(new LinearLayoutManager(getContext())); + adapter.setItems(getItems()); + rv.setAdapter(adapter); return mainView; } + + private List getItems() { + List items = new LinkedList<>(); + List savedArticles = getMyApplication().getTravelDbHelper().searchPopular(); + if (!savedArticles.isEmpty()) { + items.add(new OpenBetaTravelCard(getMyApplication(), nightMode, getFragmentManager())); + items.add(new StartEditingTravelCard(getMyApplication(), nightMode)); + items.add(getString(R.string.popular_destinations)); + + for (TravelArticle article : savedArticles) { + items.add(new ArticleTravelCard(getMyApplication(), nightMode, article, getFragmentManager())); + } + } + return items; + } } diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/explore/travelcards/ArticleTravelCard.java b/OsmAnd/src/net/osmand/plus/wikivoyage/explore/travelcards/ArticleTravelCard.java new file mode 100644 index 0000000000..471a7e39ca --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/explore/travelcards/ArticleTravelCard.java @@ -0,0 +1,134 @@ +package net.osmand.plus.wikivoyage.explore.travelcards; + +import android.graphics.drawable.Drawable; +import android.support.annotation.NonNull; +import android.support.v4.app.FragmentManager; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import com.squareup.picasso.Callback; +import com.squareup.picasso.Picasso; +import com.squareup.picasso.RequestCreator; + +import net.osmand.plus.IconsCache; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.widgets.tools.CropCircleTransformation; +import net.osmand.plus.widgets.tools.CropRectTransformation; +import net.osmand.plus.wikivoyage.WikivoyageUtils; +import net.osmand.plus.wikivoyage.article.WikivoyageArticleDialogFragment; +import net.osmand.plus.wikivoyage.data.TravelArticle; +import net.osmand.plus.wikivoyage.data.TravelLocalDataHelper; + +public class ArticleTravelCard extends BaseTravelCard { + + public static final int TYPE = 2; + public static final boolean USE_ALTERNATIVE_CARD = false; + + private TravelArticle article; + private final Drawable readIcon; + private FragmentManager fragmentManager; + + public ArticleTravelCard(OsmandApplication app, boolean nightMode, TravelArticle article, FragmentManager fragmentManager) { + super(app, nightMode); + this.article = article; + int colorId = app.getSettings().isLightContent() + ? R.color.wikivoyage_active_light : R.color.wikivoyage_active_dark; + IconsCache ic = app.getIconsCache(); + readIcon = ic.getIcon(R.drawable.ic_action_read_article, colorId); + this.fragmentManager = fragmentManager; + } + + @Override + public void bindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder) { + if (viewHolder instanceof ArticleTravelVH) { + final ArticleTravelVH holder = (ArticleTravelVH) viewHolder; + RequestCreator rc = Picasso.get() + .load(TravelArticle.getImageUrl(article.getImageTitle(), false)); + WikivoyageUtils.setupNetworkPolicy(app.getSettings(), rc); + rc.transform(USE_ALTERNATIVE_CARD ? new CropRectTransformation() : new CropCircleTransformation()) + .into(holder.icon, new Callback() { + @Override + public void onSuccess() { + holder.icon.setVisibility(View.VISIBLE); + } + + @Override + public void onError(Exception e) { + holder.icon.setVisibility(View.GONE); + } + }); + holder.title.setText(article.getTitle()); + holder.content.setText(article.getPartialContent()); + holder.partOf.setText(article.getGeoDescription()); + holder.leftButton.setText(app.getString(R.string.shared_string_read)); + View.OnClickListener readClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + if (fragmentManager != null) { + WikivoyageArticleDialogFragment.showInstance(app, fragmentManager, article.getCityId(), article.getLang()); + } + } + }; + holder.leftButton.setOnClickListener(readClickListener); + holder.itemView.setOnClickListener(readClickListener); + holder.leftButton.setCompoundDrawablesWithIntrinsicBounds(readIcon, null, null, null); + updateSaveButton(holder); + } + } + + private void updateSaveButton(final ArticleTravelVH holder) { + if (article != null) { + final TravelLocalDataHelper helper = app.getTravelDbHelper().getLocalDataHelper(); + final boolean saved = helper.isArticleSaved(article); + Drawable icon = getActiveIcon(saved ? R.drawable.ic_action_read_later_fill : R.drawable.ic_action_read_later); + holder.rightButton.setText(saved ? R.string.shared_string_delete : R.string.shared_string_save); + holder.rightButton.setCompoundDrawablesWithIntrinsicBounds(null, null, icon, null); + holder.rightButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (article != null) { + if (saved) { + helper.removeArticleFromSaved(article); + } else { + app.getTravelDbHelper().createGpxFile(article); + helper.addArticleToSaved(article); + } + updateSaveButton(holder); + } + } + }); + } + } + + public static class ArticleTravelVH extends RecyclerView.ViewHolder { + + final TextView title; + final TextView content; + final TextView partOf; + final ImageView icon; + final TextView leftButton; + final TextView rightButton; + final View divider; + final View shadow; + + public ArticleTravelVH(final View itemView) { + super(itemView); + title = (TextView) itemView.findViewById(R.id.title); + content = (TextView) itemView.findViewById(R.id.content); + partOf = (TextView) itemView.findViewById(R.id.part_of); + icon = (ImageView) itemView.findViewById(R.id.icon); + leftButton = (TextView) itemView.findViewById(R.id.left_button); + rightButton = (TextView) itemView.findViewById(R.id.right_button); + divider = itemView.findViewById(R.id.divider); + shadow = itemView.findViewById(R.id.shadow); + } + } + + @Override + public int getCardType() { + return TYPE; + } +} diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/explore/travelcards/OpenBetaTravelCard.java b/OsmAnd/src/net/osmand/plus/wikivoyage/explore/travelcards/OpenBetaTravelCard.java index 44ac5ba3f3..017a7d2542 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/explore/travelcards/OpenBetaTravelCard.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/explore/travelcards/OpenBetaTravelCard.java @@ -13,6 +13,8 @@ import net.osmand.plus.dialogs.ChoosePlanDialogFragment; public class OpenBetaTravelCard extends BaseTravelCard { + public static final int TYPE = 0; + private FragmentManager fragmentManager; public OpenBetaTravelCard(OsmandApplication app, boolean nightMode, FragmentManager fragmentManager) { @@ -37,14 +39,14 @@ public class OpenBetaTravelCard extends BaseTravelCard { } } - class OpenBetaTravelVH extends RecyclerView.ViewHolder { + public static class OpenBetaTravelVH extends RecyclerView.ViewHolder { final TextView title; final TextView description; final TextView button; final ImageView backgroundImage; - OpenBetaTravelVH(final View itemView) { + public OpenBetaTravelVH(final View itemView) { super(itemView); title = (TextView) itemView.findViewById(R.id.title); description = (TextView) itemView.findViewById(R.id.description); @@ -55,6 +57,6 @@ public class OpenBetaTravelCard extends BaseTravelCard { @Override public int getCardType() { - return 0; + return TYPE; } } diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/explore/travelcards/StartEditingTravelCard.java b/OsmAnd/src/net/osmand/plus/wikivoyage/explore/travelcards/StartEditingTravelCard.java index cce59cd217..7b57c7a51a 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/explore/travelcards/StartEditingTravelCard.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/explore/travelcards/StartEditingTravelCard.java @@ -14,6 +14,8 @@ import net.osmand.plus.R; public class StartEditingTravelCard extends BaseTravelCard { + public static final int TYPE = 1; + public StartEditingTravelCard(OsmandApplication app, boolean nightMode) { super(app, nightMode); } @@ -39,14 +41,14 @@ public class StartEditingTravelCard extends BaseTravelCard { } } - class StartEditingTravelVH extends RecyclerView.ViewHolder { + public static class StartEditingTravelVH extends RecyclerView.ViewHolder { final TextView title; final TextView description; final TextView button; final ImageView backgroundImage; - StartEditingTravelVH(final View itemView) { + public StartEditingTravelVH(final View itemView) { super(itemView); title = (TextView) itemView.findViewById(R.id.title); description = (TextView) itemView.findViewById(R.id.description); @@ -57,6 +59,6 @@ public class StartEditingTravelCard extends BaseTravelCard { @Override public int getCardType() { - return 1; + return TYPE; } }