Merge pull request #8831 from osmandapp/custom_downloads_improvements

Custom downloads improvements first part
This commit is contained in:
vshcherb 2020-04-22 22:18:52 +02:00 committed by GitHub
commit 549933f0b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 392 additions and 89 deletions

View file

@ -49,7 +49,7 @@ public class Algorithms {
return map == null || map.size() == 0; return map == null || map.size() == 0;
} }
public static boolean isEmpty(String s) { public static boolean isEmpty(CharSequence s) {
return s == null || s.length() == 0; return s == null || s.length() == 0;
} }

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<net.osmand.plus.widgets.TextViewEx 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:id="@+id/button_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:letterSpacing="@dimen/description_letter_spacing"
android:paddingLeft="@dimen/content_padding_half"
android:paddingTop="@dimen/content_padding_half"
android:paddingRight="@dimen/content_padding_half"
android:paddingBottom="@dimen/content_padding_half"
android:textColor="?attr/active_color_basic"
android:textSize="@dimen/default_desc_text_size"
app:typeface="@string/font_roboto_medium"
tools:text="@string/read_more" />

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
android:background="?attr/card_and_list_background_basic">
<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="@dimen/list_header_settings_top_margin">
<net.osmand.plus.LockableViewPager
android:id="@+id/images_pager"
android:layout_width="match_parent"
android:layout_height="132dp" />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:letterSpacing="@dimen/text_button_letter_spacing"
android:paddingLeft="@dimen/content_padding"
android:paddingTop="@dimen/list_header_settings_top_margin"
android:paddingRight="@dimen/content_padding"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/default_list_text_size"
app:typeface="@string/font_roboto_regular"
tools:text="@string/plugin_disabled_descr" />
<LinearLayout
android:id="@+id/buttons_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/content_padding_half"
android:layout_marginTop="@dimen/list_header_settings_top_margin"
android:layout_marginRight="@dimen/content_padding_half"
android:orientation="vertical" />
</LinearLayout>
</LinearLayout>

View file

@ -15,7 +15,7 @@
<ImageView <ImageView
android:id="@+id/plugin_image" android:id="@+id/plugin_image"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:adjustViewBounds="true" android:adjustViewBounds="true"

View file

@ -11,6 +11,7 @@
Thx - Hardy Thx - Hardy
--> -->
<string name="download_unsupported_action">Unsupported action %1$s</string>
<string name="extra_maps_menu_group">Extra maps</string> <string name="extra_maps_menu_group">Extra maps</string>
<string name="custom_color">Custom color</string> <string name="custom_color">Custom color</string>
<string name="lang_lmo">Lombard</string> <string name="lang_lmo">Lombard</string>

View file

@ -44,12 +44,16 @@ public class JsonUtils {
} }
public static Map<String, String> getLocalizedMapFromJson(String key, JSONObject json) throws JSONException { public static Map<String, String> getLocalizedMapFromJson(String key, JSONObject json) throws JSONException {
Map<String, String> localizedMap = new HashMap<>();
JSONObject jsonObject = json.optJSONObject(key); JSONObject jsonObject = json.optJSONObject(key);
if (jsonObject != null) { return getLocalizedMapFromJson(jsonObject);
for (Iterator<String> it = jsonObject.keys(); it.hasNext(); ) { }
public static Map<String, String> getLocalizedMapFromJson(JSONObject json) throws JSONException {
Map<String, String> localizedMap = new HashMap<>();
if (json != null) {
for (Iterator<String> it = json.keys(); it.hasNext(); ) {
String localeKey = it.next(); String localeKey = it.next();
String name = jsonObject.getString(localeKey); String name = json.getString(localeKey);
localizedMap.put(localeKey, name); localizedMap.put(localeKey, name);
} }
} }

View file

@ -3,6 +3,7 @@ package net.osmand.plus;
import android.app.Activity; import android.app.Activity;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.text.Html;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -86,8 +87,9 @@ public class CustomOsmandPlugin extends OsmandPlugin {
} }
@Override @Override
public String getDescription() { public CharSequence getDescription() {
return JsonUtils.getLocalizedResFromMap(app, descriptions, null); String description = JsonUtils.getLocalizedResFromMap(app, descriptions, null);
return description != null ? Html.fromHtml(description) : null;
} }
public String getResourceDirName() { public String getResourceDirName() {

View file

@ -1,6 +1,7 @@
package net.osmand.plus; package net.osmand.plus;
import androidx.annotation.ColorInt; import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;
import net.osmand.JsonUtils; import net.osmand.JsonUtils;
import net.osmand.PlatformUtil; import net.osmand.PlatformUtil;
@ -8,6 +9,7 @@ import net.osmand.map.WorldRegion;
import net.osmand.plus.download.CustomIndexItem; import net.osmand.plus.download.CustomIndexItem;
import net.osmand.plus.download.DownloadActivityType; import net.osmand.plus.download.DownloadActivityType;
import net.osmand.plus.download.IndexItem; import net.osmand.plus.download.IndexItem;
import net.osmand.plus.download.ui.DownloadDescriptionInfo;
import net.osmand.util.Algorithms; import net.osmand.util.Algorithms;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
@ -31,10 +33,11 @@ public class CustomRegion extends WorldRegion {
private String parentPath; private String parentPath;
private String type; private String type;
private String subfolder; private String subfolder;
private String headerButton;
private JSONArray downloadItemsJson; private JSONArray downloadItemsJson;
private DownloadDescriptionInfo descriptionInfo;
private Map<String, String> names = new HashMap<>(); private Map<String, String> names = new HashMap<>();
private Map<String, String> icons = new HashMap<>(); private Map<String, String> icons = new HashMap<>();
private Map<String, String> headers = new HashMap<>(); private Map<String, String> headers = new HashMap<>();
@ -66,6 +69,11 @@ public class CustomRegion extends WorldRegion {
return headerColor; return headerColor;
} }
@Nullable
public DownloadDescriptionInfo getDescriptionInfo() {
return descriptionInfo;
}
public static CustomRegion fromJson(JSONObject object) throws JSONException { public static CustomRegion fromJson(JSONObject object) throws JSONException {
String scopeId = object.optString("scope-id", null); String scopeId = object.optString("scope-id", null);
String path = object.optString("path", null); String path = object.optString("path", null);
@ -90,7 +98,6 @@ public class CustomRegion extends WorldRegion {
region.icons = JsonUtils.getLocalizedMapFromJson("icon", object); region.icons = JsonUtils.getLocalizedMapFromJson("icon", object);
region.headers = JsonUtils.getLocalizedMapFromJson("header", object); region.headers = JsonUtils.getLocalizedMapFromJson("header", object);
region.headerButton = object.optString("header-button", null);
region.downloadItemsJson = object.optJSONArray("items"); region.downloadItemsJson = object.optJSONArray("items");
String headerColor = object.optString("header-color", null); String headerColor = object.optString("header-color", null);
@ -99,10 +106,31 @@ public class CustomRegion extends WorldRegion {
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
region.headerColor = 0; region.headerColor = 0;
} }
region.descriptionInfo = DownloadDescriptionInfo.fromJson(object.optJSONObject("description"));
return region; return region;
} }
public JSONObject toJson() throws JSONException {
JSONObject jsonObject = new JSONObject();
jsonObject.putOpt("scope-id", scopeId);
jsonObject.putOpt("path", path);
jsonObject.putOpt("type", type);
jsonObject.putOpt("subfolder", subfolder);
JsonUtils.writeLocalizedMapToJson("name", jsonObject, names);
JsonUtils.writeLocalizedMapToJson("icon", jsonObject, icons);
JsonUtils.writeLocalizedMapToJson("header", jsonObject, headers);
if (descriptionInfo != null) {
jsonObject.putOpt("description", descriptionInfo.toJson());
}
jsonObject.putOpt("items", downloadItemsJson);
return jsonObject;
}
public List<IndexItem> loadIndexItems() { public List<IndexItem> loadIndexItems() {
List<IndexItem> items = new ArrayList<>(); List<IndexItem> items = new ArrayList<>();
if (downloadItemsJson != null) { if (downloadItemsJson != null) {
@ -152,22 +180,4 @@ public class CustomRegion extends WorldRegion {
} }
return items; return items;
} }
public JSONObject toJson() throws JSONException {
JSONObject jsonObject = new JSONObject();
jsonObject.putOpt("scope-id", scopeId);
jsonObject.putOpt("path", path);
jsonObject.putOpt("type", type);
jsonObject.putOpt("subfolder", subfolder);
jsonObject.putOpt("header-button", headerButton);
JsonUtils.writeLocalizedMapToJson("name", jsonObject, names);
JsonUtils.writeLocalizedMapToJson("icon", jsonObject, icons);
JsonUtils.writeLocalizedMapToJson("header", jsonObject, headers);
jsonObject.putOpt("items", downloadItemsJson);
return jsonObject;
}
} }

View file

@ -67,8 +67,6 @@ public class OsmAndAppCustomization {
private Map<String, Bitmap> navDrawerLogos = new HashMap<>(); private Map<String, Bitmap> navDrawerLogos = new HashMap<>();
private ArrayList<String> navDrawerParams;
private String navDrawerFooterIntent; private String navDrawerFooterIntent;
private String navDrawerFooterAppName; private String navDrawerFooterAppName;
private String navDrawerFooterPackageName; private String navDrawerFooterPackageName;
@ -254,8 +252,8 @@ public class OsmAndAppCustomization {
} }
@Nullable @Nullable
public ArrayList<String> getNavDrawerLogoParams() { public String getNavDrawerLogoUrl() {
return navDrawerParams; return app.getSettings().NAV_DRAWER_URL.get();
} }
public boolean setNavDrawerLogo(String uri, @Nullable String packageName, @Nullable String intent) { public boolean setNavDrawerLogo(String uri, @Nullable String packageName, @Nullable String intent) {
@ -300,10 +298,8 @@ public class OsmAndAppCustomization {
} catch (IOException e) { } catch (IOException e) {
LOG.error("Failed to write file", e); LOG.error("Failed to write file", e);
} }
if (packageName != null && intent != null) { if (!Algorithms.isEmpty(intent)) {
navDrawerParams = new ArrayList<>(); app.getSettings().NAV_DRAWER_LOGO.set(intent);
navDrawerParams.add(packageName);
navDrawerParams.add(intent);
} }
} }
return true; return true;

View file

@ -85,7 +85,7 @@ public abstract class OsmandPlugin {
public abstract String getName(); public abstract String getName();
public abstract String getDescription(); public abstract CharSequence getDescription();
@Nullable @Nullable
public Drawable getAssetResourceImage() { public Drawable getAssetResourceImage() {

View file

@ -1453,7 +1453,8 @@ public class OsmandSettings {
public final CommonPreference<String> API_NAV_DRAWER_ITEMS_JSON = new StringPreference("api_nav_drawer_items_json", "{}").makeGlobal(); public final CommonPreference<String> API_NAV_DRAWER_ITEMS_JSON = new StringPreference("api_nav_drawer_items_json", "{}").makeGlobal();
public final CommonPreference<String> API_CONNECTED_APPS_JSON = new StringPreference("api_connected_apps_json", "[]").makeGlobal(); public final CommonPreference<String> API_CONNECTED_APPS_JSON = new StringPreference("api_connected_apps_json", "[]").makeGlobal();
public final CommonPreference<String> NAV_DRAWER_LOGO = new StringPreference("nav_drawer_logo", "").makeProfile(); public final CommonPreference<String> NAV_DRAWER_LOGO = new StringPreference("drawer_logo", "").makeProfile();
public final CommonPreference<String> NAV_DRAWER_URL = new StringPreference("drawer_url", "").makeProfile();
public final CommonPreference<Integer> NUMBER_OF_STARTS_FIRST_XMAS_SHOWN = new IntPreference("number_of_starts_first_xmas_shown", 0).makeGlobal(); public final CommonPreference<Integer> NUMBER_OF_STARTS_FIRST_XMAS_SHOWN = new IntPreference("number_of_starts_first_xmas_shown", 0).makeGlobal();

View file

@ -981,8 +981,9 @@ public class SettingsHelper {
@Override @Override
void readItemsFromJson(@NonNull JSONObject json) throws IllegalArgumentException { void readItemsFromJson(@NonNull JSONObject json) throws IllegalArgumentException {
JSONObject prefsJson = json.optJSONObject("prefs");
try { try {
JSONObject drawerLogoJson = json.has("drawerLogo") ? json.getJSONObject("drawerLogo") : null; JSONObject drawerLogoJson = prefsJson.optJSONObject("drawer_logo");
if (drawerLogoJson != null) { if (drawerLogoJson != null) {
for (Iterator<String> it = drawerLogoJson.keys(); it.hasNext(); ) { for (Iterator<String> it = drawerLogoJson.keys(); it.hasNext(); ) {
String localeKey = it.next(); String localeKey = it.next();

View file

@ -6,6 +6,7 @@ import android.app.Dialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
@ -55,6 +56,7 @@ import net.osmand.plus.dialogs.FavoriteDialogs;
import net.osmand.plus.download.IndexItem; import net.osmand.plus.download.IndexItem;
import net.osmand.plus.liveupdates.OsmLiveActivity; import net.osmand.plus.liveupdates.OsmLiveActivity;
import net.osmand.plus.mapcontextmenu.AdditionalActionsBottomSheetDialogFragment; import net.osmand.plus.mapcontextmenu.AdditionalActionsBottomSheetDialogFragment;
import net.osmand.plus.mapcontextmenu.AdditionalActionsBottomSheetDialogFragment.ContextMenuItemClickListener;
import net.osmand.plus.mapmarkers.MapMarkersDialogFragment; import net.osmand.plus.mapmarkers.MapMarkersDialogFragment;
import net.osmand.plus.mapmarkers.MarkersPlanRouteContext; import net.osmand.plus.mapmarkers.MarkersPlanRouteContext;
import net.osmand.plus.measurementtool.MeasurementToolFragment; import net.osmand.plus.measurementtool.MeasurementToolFragment;
@ -64,11 +66,11 @@ import net.osmand.plus.routepreparationmenu.WaypointsFragment;
import net.osmand.plus.routing.RouteProvider.GPXRouteParamsBuilder; import net.osmand.plus.routing.RouteProvider.GPXRouteParamsBuilder;
import net.osmand.plus.routing.RoutingHelper; import net.osmand.plus.routing.RoutingHelper;
import net.osmand.plus.settings.BaseSettingsFragment; import net.osmand.plus.settings.BaseSettingsFragment;
import net.osmand.plus.mapcontextmenu.AdditionalActionsBottomSheetDialogFragment.ContextMenuItemClickListener;
import net.osmand.plus.views.BaseMapLayer; import net.osmand.plus.views.BaseMapLayer;
import net.osmand.plus.views.MapControlsLayer; import net.osmand.plus.views.MapControlsLayer;
import net.osmand.plus.views.MapTileLayer; import net.osmand.plus.views.MapTileLayer;
import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.OsmandMapTileView;
import net.osmand.plus.wikipedia.WikipediaDialogFragment;
import net.osmand.plus.wikivoyage.WikivoyageWelcomeDialogFragment; import net.osmand.plus.wikivoyage.WikivoyageWelcomeDialogFragment;
import net.osmand.plus.wikivoyage.data.TravelDbHelper; import net.osmand.plus.wikivoyage.data.TravelDbHelper;
import net.osmand.plus.wikivoyage.explore.WikivoyageExploreActivity; import net.osmand.plus.wikivoyage.explore.WikivoyageExploreActivity;
@ -1144,28 +1146,8 @@ public class MapActivityActions implements DialogProvider {
menu.show(); menu.show();
} }
public void restoreOrReturnDialog(final String packageName) {
AlertDialog.Builder builder = new AlertDialog.Builder(mapActivity);
builder.setTitle("Restore OsmAnd");
builder.setMessage("Do you want to Restore OsmAnd or get back to the Client App?");
builder.setPositiveButton("Restore", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
restoreOsmand();
}
});
builder.setNeutralButton("Return", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
executeHeadersIntent(packageName);
}
});
builder.setNegativeButton("Cancel", null);
builder.show();
}
protected void updateDrawerMenu() { protected void updateDrawerMenu() {
boolean nightMode = getMyApplication().getDaynightHelper().isNightModeForMapControls(); final boolean nightMode = getMyApplication().getDaynightHelper().isNightModeForMapControls();
final ListView menuItemsListView = (ListView) mapActivity.findViewById(R.id.menuItems); final ListView menuItemsListView = (ListView) mapActivity.findViewById(R.id.menuItems);
if (nightMode) { if (nightMode) {
menuItemsListView.setBackgroundColor(ContextCompat.getColor(mapActivity, R.color.list_background_color_dark)); menuItemsListView.setBackgroundColor(ContextCompat.getColor(mapActivity, R.color.list_background_color_dark));
@ -1174,7 +1156,6 @@ public class MapActivityActions implements DialogProvider {
} }
menuItemsListView.removeHeaderView(drawerLogoHeader); menuItemsListView.removeHeaderView(drawerLogoHeader);
Bitmap navDrawerLogo = getMyApplication().getAppCustomization().getNavDrawerLogo(); Bitmap navDrawerLogo = getMyApplication().getAppCustomization().getNavDrawerLogo();
final ArrayList<String> navDrawerLogoParams = getMyApplication().getAppCustomization().getNavDrawerLogoParams();
if (navDrawerLogo != null) { if (navDrawerLogo != null) {
drawerLogoHeader.setImageBitmap(navDrawerLogo); drawerLogoHeader.setImageBitmap(navDrawerLogo);
@ -1193,8 +1174,9 @@ public class MapActivityActions implements DialogProvider {
boolean hasHeader = menuItemsListView.getHeaderViewsCount() > 0; boolean hasHeader = menuItemsListView.getHeaderViewsCount() > 0;
boolean hasFooter = menuItemsListView.getFooterViewsCount() > 0; boolean hasFooter = menuItemsListView.getFooterViewsCount() > 0;
if (hasHeader && position == 0 || (hasFooter && position == menuItemsListView.getCount() - 1)) { if (hasHeader && position == 0 || (hasFooter && position == menuItemsListView.getCount() - 1)) {
if (navDrawerLogoParams != null) { String drawerLogoParams = getMyApplication().getAppCustomization().getNavDrawerLogoUrl();
executeHeadersIntent(navDrawerLogoParams.get(0)); if (!Algorithms.isEmpty(drawerLogoParams)) {
WikipediaDialogFragment.showFullArticle(mapActivity, Uri.parse(drawerLogoParams), nightMode);
} }
} else { } else {
position -= menuItemsListView.getHeaderViewsCount(); position -= menuItemsListView.getHeaderViewsCount();
@ -1209,20 +1191,4 @@ public class MapActivityActions implements DialogProvider {
}); });
} }
private void executeHeadersIntent(String packageName) {
Intent launchIntent = mapActivity.getPackageManager().getLaunchIntentForPackage(packageName);
if(launchIntent!=null) mapActivity.startActivity(launchIntent);
mapActivity.closeDrawer();
}
private void showReturnConfirmationDialog(String packageName) {
restoreOrReturnDialog(packageName);
mapActivity.closeDrawer();
}
private void restoreOsmand(){
getMyApplication().getAppCustomization().restoreOsmand();
mapActivity.closeDrawer();
}
} }

View file

@ -7,6 +7,7 @@ import android.graphics.drawable.Drawable;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.text.method.LinkMovementMethod;
import android.util.Log; import android.util.Log;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
@ -15,9 +16,11 @@ import android.widget.CompoundButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import net.osmand.AndroidUtils;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.OsmandPlugin; import net.osmand.plus.OsmandPlugin;
import net.osmand.plus.R; import net.osmand.plus.R;
@ -74,6 +77,14 @@ public class PluginActivity extends OsmandActionBarActivity implements Download
TextView descriptionView = (TextView) findViewById(R.id.plugin_description); TextView descriptionView = (TextView) findViewById(R.id.plugin_description);
descriptionView.setText(plugin.getDescription()); descriptionView.setText(plugin.getDescription());
boolean light = getMyApplication().getSettings().isLightContent();
int linkTextColor = ContextCompat.getColor(this,
light ? R.color.ctx_menu_bottom_view_url_color_light : R.color.ctx_menu_bottom_view_url_color_dark);
descriptionView.setLinkTextColor(linkTextColor);
descriptionView.setMovementMethod(LinkMovementMethod.getInstance());
AndroidUtils.removeLinkUnderline(descriptionView);
Button settingsButton = (Button) findViewById(R.id.plugin_settings); Button settingsButton = (Button) findViewById(R.id.plugin_settings);
settingsButton.setOnClickListener(new View.OnClickListener() { settingsButton.setOnClickListener(new View.OnClickListener() {
@Override @Override

View file

@ -5,6 +5,7 @@ import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.os.Bundle; import android.os.Bundle;
import android.text.method.LinkMovementMethod;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -17,6 +18,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.PopupMenu; import androidx.appcompat.widget.PopupMenu;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import net.osmand.AndroidUtils; import net.osmand.AndroidUtils;
@ -182,6 +184,14 @@ public class PluginsActivity extends OsmandListActivity implements DownloadIndex
name = plugin.getName(); name = plugin.getName();
pluginDescription.setText(plugin.getDescription()); pluginDescription.setText(plugin.getDescription());
boolean light = getMyApplication().getSettings().isLightContent();
int linkTextColor = ContextCompat.getColor(PluginsActivity.this,
light ? R.color.ctx_menu_bottom_view_url_color_light : R.color.ctx_menu_bottom_view_url_color_dark);
pluginDescription.setLinkTextColor(linkTextColor);
pluginDescription.setMovementMethod(LinkMovementMethod.getInstance());
AndroidUtils.removeLinkUnderline(pluginDescription);
OsmandApplication app = getMyApplication(); OsmandApplication app = getMyApplication();
int color = AndroidUtils.getColorFromAttr(app, R.attr.list_background_color); int color = AndroidUtils.getColorFromAttr(app, R.attr.list_background_color);
pluginLogo.setImageDrawable(UiUtilities.tintDrawable(plugin.getLogoResource(), color)); pluginLogo.setImageDrawable(UiUtilities.tintDrawable(plugin.getLogoResource(), color));

View file

@ -110,8 +110,9 @@ public class BottomSheetItemWithDescription extends SimpleBottomSheetItem {
return this; return this;
} }
public void setDescriptionLinksClickable(boolean descriptionLinksClickable) { public Builder setDescriptionLinksClickable(boolean descriptionLinksClickable) {
this.descriptionLinksClickable = descriptionLinksClickable; this.descriptionLinksClickable = descriptionLinksClickable;
return this;
} }
public BottomSheetItemWithDescription create() { public BottomSheetItemWithDescription create() {

View file

@ -109,6 +109,7 @@ public class PluginInstalledBottomSheetDialog extends MenuBottomSheetDialogFragm
setupHeightAndBackground(getView()); setupHeightAndBackground(getView());
} }
}) })
.setDescriptionLinksClickable(true)
.setDescription(plugin.getDescription()) .setDescription(plugin.getDescription())
.setDescriptionMaxLines(COLLAPSED_DESCRIPTION_LINES) .setDescriptionMaxLines(COLLAPSED_DESCRIPTION_LINES)
.setLayoutId(R.layout.bottom_sheet_item_with_expandable_descr) .setLayoutId(R.layout.bottom_sheet_item_with_expandable_descr)

View file

@ -470,7 +470,7 @@ public class DownloadResources extends DownloadResourceGroup {
mainGrp.addGroup(flatFiles); mainGrp.addGroup(flatFiles);
} }
} }
DownloadResourceGroup subRegions = new DownloadResourceGroup(mainGrp, DownloadResourceGroupType.SUBREGIONS); DownloadResourceGroup subRegions = new DownloadResourceGroup(mainGrp, DownloadResourceGroupType.EXTRA_MAPS);
mainGrp.addGroup(subRegions); mainGrp.addGroup(subRegions);
// add to processing queue // add to processing queue
for (WorldRegion rg : subregions) { for (WorldRegion rg : subregions) {

View file

@ -0,0 +1,112 @@
package net.osmand.plus.download.ui;
import android.content.Context;
import android.text.Html;
import net.osmand.JsonUtils;
import net.osmand.PlatformUtil;
import org.apache.commons.logging.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class DownloadDescriptionInfo {
private static final Log LOG = PlatformUtil.getLog(DownloadDescriptionInfo.class);
private JSONArray buttonsJson;
private List<String> imageUrls;
private Map<String, String> texts;
public List<String> getImageUrls() {
return imageUrls;
}
public CharSequence getLocalizedDescription(Context ctx) {
String description = JsonUtils.getLocalizedResFromMap(ctx, texts, null);
return description != null ? Html.fromHtml(description) : null;
}
public List<ActionButton> getActionButtons(Context ctx) {
List<ActionButton> actionButtons = new ArrayList<>();
if (buttonsJson != null) {
try {
for (int i = 0; i < buttonsJson.length(); i++) {
String url = null;
String actionType = null;
JSONObject object = buttonsJson.getJSONObject(i);
if (object.has("url")) {
url = object.optString("url");
} else if (object.has("action")) {
actionType = object.optString("action");
}
Map<String, String> localizedMap = JsonUtils.getLocalizedMapFromJson(object);
String name = JsonUtils.getLocalizedResFromMap(ctx, localizedMap, null);
ActionButton actionButton = new ActionButton(actionType, name, url);
actionButtons.add(actionButton);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
return actionButtons;
}
public static DownloadDescriptionInfo fromJson(JSONObject json) {
if (json != null) {
DownloadDescriptionInfo downloadDescriptionInfo = new DownloadDescriptionInfo();
try {
downloadDescriptionInfo.texts = JsonUtils.getLocalizedMapFromJson("text", json);
downloadDescriptionInfo.imageUrls = JsonUtils.jsonArrayToList("image", json);
downloadDescriptionInfo.buttonsJson = json.optJSONArray("button");
} catch (JSONException e) {
LOG.error(e);
}
return downloadDescriptionInfo;
}
return null;
}
public JSONObject toJson() throws JSONException {
JSONObject descrJson = new JSONObject();
JsonUtils.writeLocalizedMapToJson("text", descrJson, texts);
JsonUtils.writeStringListToJson("image", descrJson, imageUrls);
descrJson.putOpt("button", buttonsJson);
return descrJson;
}
public static class ActionButton {
private String actionType;
private String name;
private String url;
public ActionButton(String actionType, String name, String url) {
this.actionType = actionType;
this.name = name;
this.url = url;
}
public String getActionType() {
return actionType;
}
public String getName() {
return name;
}
public String getUrl() {
return url;
}
}
}

View file

@ -5,6 +5,7 @@ import android.app.ProgressDialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.util.TypedValue; import android.util.TypedValue;
@ -21,15 +22,22 @@ import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import androidx.core.view.MenuItemCompat; import androidx.core.view.MenuItemCompat;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import androidx.viewpager.widget.PagerAdapter;
import com.squareup.picasso.Callback;
import com.squareup.picasso.Picasso;
import net.osmand.AndroidNetworkUtils; import net.osmand.AndroidNetworkUtils;
import net.osmand.AndroidUtils; import net.osmand.AndroidUtils;
import net.osmand.PicassoUtils;
import net.osmand.plus.CustomRegion;
import net.osmand.plus.LockableViewPager;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.OsmandSettings;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.UiUtilities; import net.osmand.plus.UiUtilities;
import net.osmand.plus.activities.OsmandBaseExpandableListAdapter; import net.osmand.plus.activities.OsmandBaseExpandableListAdapter;
@ -43,9 +51,12 @@ import net.osmand.plus.download.DownloadResourceGroup.DownloadResourceGroupType;
import net.osmand.plus.download.DownloadResources; import net.osmand.plus.download.DownloadResources;
import net.osmand.plus.download.DownloadValidationManager; import net.osmand.plus.download.DownloadValidationManager;
import net.osmand.plus.download.IndexItem; import net.osmand.plus.download.IndexItem;
import net.osmand.plus.download.ui.DownloadDescriptionInfo.ActionButton;
import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.inapp.InAppPurchaseHelper; import net.osmand.plus.inapp.InAppPurchaseHelper;
import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseListener; import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseListener;
import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseTaskType; import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseTaskType;
import net.osmand.plus.wikipedia.WikipediaDialogFragment;
import net.osmand.util.Algorithms; import net.osmand.util.Algorithms;
import org.json.JSONException; import org.json.JSONException;
@ -77,13 +88,15 @@ public class DownloadResourceGroupFragment extends DialogFragment implements Dow
private View searchView; private View searchView;
private View restorePurchasesView; private View restorePurchasesView;
private View subscribeEmailView; private View subscribeEmailView;
private View descriptionView;
private boolean nightMode;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
purchaseHelper = getDownloadActivity().getPurchaseHelper(); purchaseHelper = getDownloadActivity().getPurchaseHelper();
boolean isLightTheme = getMyApplication().getSettings().OSMAND_THEME.get() == OsmandSettings.OSMAND_LIGHT_THEME; nightMode = !getMyApplication().getSettings().isLightContent();
int themeId = isLightTheme ? R.style.OsmandLightTheme : R.style.OsmandDarkTheme; int themeId = nightMode ? R.style.OsmandDarkTheme : R.style.OsmandLightTheme;
setStyle(STYLE_NO_FRAME, themeId); setStyle(STYLE_NO_FRAME, themeId);
setHasOptionsMenu(true); setHasOptionsMenu(true);
} }
@ -133,6 +146,7 @@ public class DownloadResourceGroupFragment extends DialogFragment implements Dow
addSubscribeEmailRow(); addSubscribeEmailRow();
addSearchRow(); addSearchRow();
addRestorePurchasesRow(); addRestorePurchasesRow();
addDescriptionRow();
listView.setOnChildClickListener(this); listView.setOnChildClickListener(this);
listAdapter = new DownloadResourceGroupAdapter(activity); listAdapter = new DownloadResourceGroupAdapter(activity);
listView.setAdapter(listAdapter); listView.setAdapter(listAdapter);
@ -180,6 +194,11 @@ public class DownloadResourceGroupFragment extends DialogFragment implements Dow
} }
} }
private void addDescriptionRow() {
descriptionView = activity.getLayoutInflater().inflate(R.layout.group_description_item, listView, false);
listView.addHeaderView(descriptionView);
}
private void addSearchRow() { private void addSearchRow() {
if (!openAsDialog() ) { if (!openAsDialog() ) {
searchView = activity.getLayoutInflater().inflate(R.layout.simple_list_menu_item, null); searchView = activity.getLayoutInflater().inflate(R.layout.simple_list_menu_item, null);
@ -231,6 +250,53 @@ public class DownloadResourceGroupFragment extends DialogFragment implements Dow
} }
} }
private void updateDescriptionView() {
if (descriptionView != null) {
if (group != null && group.getRegion() instanceof CustomRegion) {
CustomRegion customRegion = (CustomRegion) group.getRegion();
DownloadDescriptionInfo downloadDescriptionInfo = customRegion.getDescriptionInfo();
if (downloadDescriptionInfo != null) {
TextView description = descriptionView.findViewById(R.id.description);
CharSequence descr = downloadDescriptionInfo.getLocalizedDescription(activity);
description.setText(descr);
AndroidUiHelper.updateVisibility(description, !Algorithms.isEmpty(descr));
ViewGroup buttonsContainer = descriptionView.findViewById(R.id.buttons_container);
buttonsContainer.removeAllViews();
for (final ActionButton actionButton : downloadDescriptionInfo.getActionButtons(activity)) {
String name = actionButton.getName();
if (!Algorithms.isEmpty(name)) {
TextView buttonText = (TextView) activity.getLayoutInflater().inflate(R.layout.download_description_button, buttonsContainer, false);
buttonText.setText(name);
buttonText.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (actionButton.getUrl() != null) {
WikipediaDialogFragment.showFullArticle(activity, Uri.parse(actionButton.getUrl()), nightMode);
} else {
activity.getMyApplication().showShortToastMessage(R.string.download_unsupported_action, actionButton.getActionType());
}
}
});
buttonsContainer.addView(buttonText);
}
}
LockableViewPager viewPager = descriptionView.findViewById(R.id.images_pager);
if (!Algorithms.isEmpty(downloadDescriptionInfo.getImageUrls())) {
ImagesPagerAdapter adapter = new ImagesPagerAdapter(downloadDescriptionInfo.getImageUrls());
viewPager.setAdapter(adapter);
} else {
viewPager.setVisibility(View.GONE);
}
descriptionView.findViewById(R.id.container).setVisibility(View.VISIBLE);
return;
}
}
descriptionView.findViewById(R.id.container).setVisibility(View.GONE);
}
}
private void hideSubscribeEmailView() { private void hideSubscribeEmailView() {
if (subscribeEmailView != null && subscribeEmailView.findViewById(R.id.container).getVisibility() == View.VISIBLE) { if (subscribeEmailView != null && subscribeEmailView.findViewById(R.id.container).getVisibility() == View.VISIBLE) {
subscribeEmailView.findViewById(R.id.container).setVisibility(View.GONE); subscribeEmailView.findViewById(R.id.container).setVisibility(View.GONE);
@ -389,12 +455,15 @@ public class DownloadResourceGroupFragment extends DialogFragment implements Dow
} }
private void reloadData() { private void reloadData() {
DownloadResources indexes = activity.getDownloadThread().getIndexes();
group = indexes.getGroupById(groupId);
if (!openAsDialog()) { if (!openAsDialog()) {
updateSearchView(); updateSearchView();
} }
updateSubscribeEmailView(); updateSubscribeEmailView();
DownloadResources indexes = activity.getDownloadThread().getIndexes(); updateDescriptionView();
group = indexes.getGroupById(groupId);
if (group != null) { if (group != null) {
listAdapter.update(group); listAdapter.update(group);
toolbar.setTitle(group.getName(activity)); toolbar.setTitle(group.getName(activity));
@ -729,4 +798,60 @@ public class DownloadResourceGroupFragment extends DialogFragment implements Dow
return true; return true;
} }
} }
private class ImagesPagerAdapter extends PagerAdapter {
private PicassoUtils picassoUtils;
private List<String> imageUrls;
public ImagesPagerAdapter(List<String> imageUrls) {
this.imageUrls = imageUrls;
picassoUtils = PicassoUtils.getPicasso(getMyApplication());
}
@Override
public int getCount() {
return imageUrls.size();
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View view = createImageView(position);
container.addView(view, 0);
return view;
}
@Override
public void destroyItem(ViewGroup collection, int position, @NonNull Object view) {
collection.removeView((View) view);
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
private View createImageView(int position) {
final ImageView imageView = new ImageView(getContext());
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
final String imageUrl = imageUrls.get(position);
Picasso.get().load(imageUrl).into(imageView, new Callback() {
@Override
public void onSuccess() {
imageView.setVisibility(View.VISIBLE);
picassoUtils.setResultLoaded(imageUrl, true);
}
@Override
public void onError(Exception e) {
imageView.setVisibility(View.INVISIBLE);
picassoUtils.setResultLoaded(imageUrl, false);
}
});
return imageView;
}
}
} }