[new downloader][android] fix: More downloader functionality.

This commit is contained in:
Alexander Marchuk 2016-02-11 15:32:33 +03:00 committed by Sergey Yershov
parent 8d115e2255
commit a1f13ae29a
9 changed files with 259 additions and 34 deletions

View file

@ -10,7 +10,8 @@
android:background="?attr/clickableBackground"
android:paddingLeft="@dimen/margin_half"
android:paddingRight="@dimen/margin_half"
android:gravity="center_vertical">
android:gravity="center_vertical"
android:baselineAligned="false">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"

View file

@ -39,5 +39,14 @@
<attr name="menuBackgroundOpen" format="color"/>
<attr name="myPositionButtonAnimation" format="reference"/>
<attr name="downloaderTheme" format="reference"/>
</declare-styleable>
<declare-styleable name="ThemeAttrs.Downloader">
<attr name="status_done" format="reference"/>
<attr name="status_downloadable" format="reference"/>
<attr name="status_failed" format="reference"/>
<attr name="status_updatable" format="reference"/>
</declare-styleable>
</resources>

View file

@ -56,6 +56,8 @@
<item name="menuBackgroundOpen">@color/bg_menu_open</item>
<item name="myPositionButtonAnimation">@drawable/anim_myposition_pending</item>
<item name="downloaderTheme">@style/MwmTheme.Downloader</item>
</style>
<!-- Night theme -->
@ -114,5 +116,7 @@
<item name="menuBackgroundOpen">@color/bg_menu_open_night</item>
<item name="myPositionButtonAnimation">@drawable/anim_myposition_pending_night</item>
<item name="downloaderTheme">@style/MwmTheme.Downloader.Night</item>
</style>
</resources>

View file

@ -97,4 +97,17 @@
<item name="bs_closeDrawable">@drawable/bs_ic_clear</item>
<item name="bs_moreDrawable">@drawable/bs_ic_more</item>
</style>
<style name="MwmTheme.Downloader">
<item name="status_done">@drawable/downloader_done</item>
<item name="status_downloadable">@drawable/downloader_download</item>
<item name="status_failed">@drawable/downloader_failed</item>
<item name="status_updatable">@drawable/downloader_update</item>
</style>
<style name="MwmTheme.Downloader.Night">
<item name="status_done">@drawable/downloader_done_night</item>
<item name="status_failed">@drawable/downloader_failed_night</item>
<item name="status_updatable">@drawable/downloader_update_night</item>
</style>
</resources>

View file

@ -1,10 +1,14 @@
package com.mapswithme.maps.downloader;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.support.annotation.AttrRes;
import android.support.annotation.DrawableRes;
import android.support.annotation.StringRes;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -21,6 +25,8 @@ import java.util.Stack;
import com.mapswithme.maps.MwmApplication;
import com.mapswithme.maps.R;
import com.mapswithme.maps.widget.WheelProgressView;
import com.mapswithme.util.BottomSheetHelper;
import com.mapswithme.util.ThemeUtils;
import com.mapswithme.util.UiUtils;
import com.timehop.stickyheadersrecyclerview.StickyRecyclerHeadersAdapter;
import com.timehop.stickyheadersrecyclerview.StickyRecyclerHeadersDecoration;
@ -31,6 +37,7 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
implements StickyRecyclerHeadersAdapter<DownloaderAdapter.HeaderViewHolder>
{
private final RecyclerView mRecycler;
private final Activity mActivity;
private final StickyRecyclerHeadersDecoration mHeadersDecoration;
private final List<CountryItem> mItems = new ArrayList<>();
@ -39,8 +46,64 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
private final SparseArray<String> mHeaders = new SparseArray<>();
private final Stack<PathEntry> mPath = new Stack<>();
private final SparseIntArray mIconsCache = new SparseIntArray();
private int mListenerSlot;
private enum MenuItem
{
DELETE(R.drawable.ic_delete, R.string.delete)
{
@Override
void invoke(CountryItem item)
{
CANCEL.invoke(item);
MapManager.nativeDelete(item.id);
}
},
CANCEL(R.drawable.ic_cancel, R.string.cancel)
{
@Override
void invoke(CountryItem item)
{
MapManager.nativeCancel(item.id);
}
},
EXPLORE(R.drawable.ic_explore, R.string.zoom_to_country)
{
@Override
void invoke(CountryItem item)
{
}
},
UPDATE(R.drawable.ic_update, R.string.downloader_update_map)
{
@Override
void invoke(CountryItem item)
{
MapManager.nativeGetAttributes(item);
if (item.status == CountryItem.STATUS_UPDATABLE)
MapManager.nativeUpdate(item.id);
}
};
final @DrawableRes int icon;
final @StringRes int title;
MenuItem(int icon, int title)
{
this.icon = icon;
this.title = title;
}
abstract void invoke(CountryItem item);
}
private static class PathEntry
{
final String countryId;
@ -65,6 +128,7 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
return;
MapManager.nativeGetAttributes(ci);
// TODO
}
@Override
@ -75,6 +139,7 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
return;
MapManager.nativeGetAttributes(ci);
// TODO
}
};
@ -89,7 +154,88 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
private CountryItem mItem;
@SuppressLint("WrongViewCast")
private void processClick()
{
switch (mItem.status)
{
case CountryItem.STATUS_DONE:
processLongClick();
break;
case CountryItem.STATUS_DOWNLOADABLE:
MapManager.nativeDownload(mItem.id);
break;
case CountryItem.STATUS_FAILED:
MapManager.nativeRetry(mItem.id);
break;
case CountryItem.STATUS_PROGRESS:
case CountryItem.STATUS_ENQUEUED:
MapManager.nativeCancel(mItem.id);
break;
case CountryItem.STATUS_UPDATABLE:
MapManager.nativeUpdate(mItem.id);
break;
default:
throw new IllegalArgumentException("Inappropriate item status: " + mItem.status);
}
}
private void processLongClick()
{
List<MenuItem> items = new ArrayList<>();
switch (mItem.status)
{
case CountryItem.STATUS_UPDATABLE:
items.add(MenuItem.UPDATE);
// No break
case CountryItem.STATUS_DONE:
items.add(MenuItem.EXPLORE);
items.add(MenuItem.DELETE);
break;
case CountryItem.STATUS_FAILED:
items.add(MenuItem.CANCEL);
if (mItem.present)
{
items.add(MenuItem.DELETE);
items.add(MenuItem.EXPLORE);
}
break;
case CountryItem.STATUS_PROGRESS:
case CountryItem.STATUS_ENQUEUED:
items.add(MenuItem.CANCEL);
if (mItem.present)
items.add(MenuItem.EXPLORE);
break;
}
if (items.isEmpty())
return;
BottomSheetHelper.Builder bs = BottomSheetHelper.create(mActivity, mItem.name);
for (MenuItem item: items)
bs.sheet(item.ordinal(), item.icon, item.title);
bs.listener(new android.view.MenuItem.OnMenuItemClickListener()
{
@Override
public boolean onMenuItemClick(android.view.MenuItem item)
{
MenuItem.values()[item.getItemId()].invoke(mItem);
return false;
}
}).tint().show();
}
public ViewHolder(View frame)
{
super(frame);
@ -106,12 +252,23 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
@Override
public void onClick(View v)
{
if (mItem.totalChildCount > 0)
if (mItem.isExpandable())
{
goDeeper(mItem.id);
return;
}
processClick();
}
});
frame.setOnLongClickListener(new View.OnLongClickListener()
{
@Override
public boolean onLongClick(View v)
{
processLongClick();
return true;
}
});
@ -120,7 +277,16 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
@Override
public void onClick(View v)
{
MapManager.nativeGetAttributes(mItem);
processClick();
}
});
mProgress.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
MapManager.nativeCancel(mItem.id);
}
});
}
@ -138,36 +304,44 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
return;
}
boolean clickable = mItem.isExpandable();
@AttrRes int iconAttr;
switch (mItem.status)
{
case CountryItem.STATUS_DONE:
mStatus.setClickable(false);
clickable = false;
iconAttr = R.attr.status_done;
break;
case CountryItem.STATUS_DOWNLOADABLE:
mStatus.setClickable(true);
iconAttr = R.attr.status_downloadable;
break;
case CountryItem.STATUS_FAILED:
mStatus.setClickable(true);
iconAttr = R.attr.status_failed;
break;
case CountryItem.STATUS_ENQUEUED:
mStatus.setClickable(false);
clickable = false;
iconAttr = R.attr.status_updatable;
break;
case CountryItem.STATUS_UPDATABLE:
mStatus.setImageResource(R.drawable.downloader_update);
mStatus.setClickable(true);
iconAttr = R.attr.status_updatable;
break;
case CountryItem.STATUS_MIXED:
// TODO (trashkalmar): Status will be replaced with something less senseless
iconAttr = R.attr.status_updatable;
break;
default:
throw new IllegalArgumentException("Inappropriate item status: " + mItem.status);
}
mStatus.setClickable(clickable);
mStatus.setImageResource(resolveIcon(iconAttr));
}
void bind(CountryItem item)
@ -192,6 +366,18 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
}
}
private int resolveIcon(@AttrRes int iconAttr)
{
int res = mIconsCache.get(iconAttr);
if (res == 0)
{
res = ThemeUtils.getResource(mActivity, R.attr.downloaderTheme, iconAttr);
mIconsCache.put(iconAttr, res);
}
return res;
}
private void collectHeaders()
{
mHeaders.clear();
@ -246,13 +432,13 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
mCountryIndex.put(ci.id, ci);
mHeadersDecoration.invalidateHeaders();
//mRecycler.scrollToPosition(0);
notifyDataSetChanged();
}
DownloaderAdapter(RecyclerView recycler)
DownloaderAdapter(RecyclerView recycler, Activity activity)
{
mRecycler = recycler;
mActivity = activity;
mHeadersDecoration = new StickyRecyclerHeadersDecoration(this);
mRecycler.addItemDecoration(mHeadersDecoration);
refreshData();
@ -261,7 +447,7 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
View frame = LayoutInflater.from(parent.getContext()).inflate(R.layout.downloader_item, parent, false);
View frame = LayoutInflater.from(mActivity).inflate(R.layout.downloader_item, parent, false);
return new ViewHolder(frame);
}
@ -274,7 +460,7 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
@Override
public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent)
{
View frame = LayoutInflater.from(parent.getContext()).inflate(R.layout.downloader_item_header, parent, false);
View frame = LayoutInflater.from(mActivity).inflate(R.layout.downloader_item_header, parent, false);
return new HeaderViewHolder(frame);
}
@ -306,12 +492,12 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
refreshData();
}
public boolean canGoUpdwards()
boolean canGoUpdwards()
{
return !mPath.isEmpty();
}
public boolean goUpwards()
boolean goUpwards()
{
if (!canGoUpdwards())
return false;

View file

@ -214,7 +214,7 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment
{
if (mAdapter == null)
{
mAdapter = new DownloaderAdapter(getRecyclerView());
mAdapter = new DownloaderAdapter(getRecyclerView(), getActivity());
mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver()
{
@Override

View file

@ -10,6 +10,6 @@ public class SelectMigrationFragment extends BaseMwmRecyclerFragment
protected RecyclerView.Adapter createAdapter()
{
// TODO customize download adapter to allow selections
return new DownloaderAdapter(getRecyclerView());
return new DownloaderAdapter(getRecyclerView(), getActivity());
}
}

View file

@ -4,12 +4,6 @@ import android.annotation.TargetApi;
import android.os.Build;
import android.util.Log;
import com.mapswithme.maps.BuildConfig;
import com.mapswithme.maps.Framework;
import com.mapswithme.maps.MwmApplication;
import com.mapswithme.util.Constants;
import com.mapswithme.util.Utils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
@ -21,7 +15,13 @@ import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;
public final class StorageUtils
import com.mapswithme.maps.BuildConfig;
import com.mapswithme.maps.Framework;
import com.mapswithme.maps.MwmApplication;
import com.mapswithme.util.Constants;
import com.mapswithme.util.Utils;
final class StorageUtils
{
private StorageUtils() {}
@ -81,7 +81,7 @@ public final class StorageUtils
// http://stackoverflow.com/questions/8151779/find-sd-card-volume-label-on-android
// http://stackoverflow.com/questions/5694933/find-an-external-sd-card-location
// http://stackoverflow.com/questions/14212969/file-canwrite-returns-false-on-some-devices-although-write-external-storage-pe
static void parseMountFile(String file, int mode, List<String> paths)
private static void parseMountFile(String file, int mode, List<String> paths)
{
Log.i(StoragePathManager.TAG, "Parsing " + file);
@ -207,14 +207,12 @@ public final class StorageUtils
}
} finally
{
if (inputChannel != null)
inputChannel.close();
if (outputChannel != null)
outputChannel.close();
Utils.closeStream(inputChannel);
Utils.closeStream(outputChannel);
}
}
static long getDirSizeRecursively(File file, FilenameFilter fileFilter)
private static long getDirSizeRecursively(File file, FilenameFilter fileFilter)
{
if (file.isDirectory())
{
@ -264,7 +262,7 @@ public final class StorageUtils
}
@SuppressWarnings("ResultOfMethodCallIgnored")
static void removeEmptyDirectories(File dir)
private static void removeEmptyDirectories(File dir)
{
for (File file : dir.listFiles())
{

View file

@ -1,6 +1,7 @@
package com.mapswithme.util;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.AttrRes;
import android.support.annotation.ColorInt;
import android.support.annotation.StyleRes;
@ -32,11 +33,24 @@ public final class ThemeUtils
public static int getResource(Context context, @AttrRes int attr)
{
if (!context.getTheme().resolveAttribute(attr, VALUE_BUFFER, true))
throw new IllegalArgumentException("Failed to resolve drawable theme attribute");
throw new IllegalArgumentException("Failed to resolve theme attribute");
return VALUE_BUFFER.resourceId;
}
public static int getResource(Context context, @AttrRes int style, @AttrRes int attr)
{
int styleRef = getResource(context, style);
int[] attrs = new int[] { attr };
TypedArray ta = context.getTheme().obtainStyledAttributes(styleRef, attrs);
ta.getValue(0, VALUE_BUFFER);
ta.recycle();
return VALUE_BUFFER.resourceId;
}
public static LayoutInflater themedInflater(LayoutInflater src, @StyleRes int theme)
{
Context wrapper = new ContextThemeWrapper(src.getContext(), theme);