[AND] Refactoring of DowloadUi

This commit is contained in:
Dmitry Kunin 2013-10-21 18:17:48 +03:00 committed by Alex Zolotarev
parent 0fcc1f38e7
commit 80c34d0f98
26 changed files with 901 additions and 866 deletions

View file

@ -190,7 +190,7 @@
android:theme="@style/MWMMain.NoBar" >
</activity>
<activity
android:name="com.mapswithme.maps.DownloadUI"
android:name="com.mapswithme.country.DownloadUI"
android:configChanges="orientation|screenLayout|screenSize"
android:label="@string/download_maps"
android:noHistory="true"

Binary file not shown.

After

Width:  |  Height:  |  Size: 683 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 860 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 772 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 671 B

View file

@ -1,70 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingLeft="4dp"
android:paddingRight="8dp" >
android:paddingRight="4dp" >
<FrameLayout
android:id="@+id/flagOrGuide"
android:layout_width="wrap_content"
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:padding="5dip"
android:visibility="visible" >
android:layout_gravity="center" >
<!-- Visible by defalut -->
<ImageView
android:id="@+id/country_flag"
<FrameLayout
android:id="@+id/flagContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:scaleType="center"
android:visibility="visible" />
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:padding="5dip"
android:visibility="visible" >
<!-- Invisible by default -->
<ImageView
android:id="@+id/country_flag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:scaleType="center"
android:src="@drawable/jp" />
<ImageView
android:id="@+id/guide_available"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:scaleType="center"
android:visibility="gone" />
</FrameLayout>
<!-- Invisible by default -->
<ImageView
android:id="@+id/show_country"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:padding="10dip"
android:scaleType="center"
android:src="@drawable/goto_map" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/show_country"
android:layout_toRightOf="@+id/flagOrGuide"
android:orientation="vertical" >
<ImageView
android:id="@+id/guide_available"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|left"
android:src="@drawable/ic_guide_mark" />
</FrameLayout>
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/country_menu"
android:layout_toRightOf="@id/flagContainer"
android:maxLines="2"
android:text="Andora Andora Andora Andora"
android:textAppearance="@style/Holo.TextAppearance.Large.Light" />
<TextView
android:id="@+id/summary"
<ImageView
android:id="@+id/country_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/Holo.TextAppearance.Small.Light" />
</LinearLayout>
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:padding="10dip"
android:scaleType="center"
android:src="@drawable/ic_county_menu" />
</RelativeLayout>
</RelativeLayout>
<ProgressBar
android:id="@+id/download_progress"
style="@style/Holo.ProgressBar.Horizontal.Light"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginBottom="-2dp"
android:max="100"
android:padding="0" />
</FrameLayout>

View file

@ -1,12 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menuitem_about_dialog"
android:title="@string/about"
android:icon="@drawable/ic_menu_about"
android:showAsAction="ifRoom"/>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:id="@+id/menuitem_settings_activity"
android:title="@string/settings"
android:icon="@android:drawable/ic_menu_preferences"
android:showAsAction="ifRoom"/>
</menu>
<item
android:id="@+id/menuitem_about_dialog"
android:icon="@drawable/ic_about"
android:showAsAction="ifRoom"
android:title="@string/about"/>
<item
android:id="@+id/menuitem_settings_activity"
android:icon="@drawable/ic_settings"
android:showAsAction="ifRoom"
android:title="@string/settings"/>
</menu>

View file

@ -0,0 +1,628 @@
package com.mapswithme.country;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.mapswithme.maps.Framework;
import com.mapswithme.maps.MWMApplication;
import com.mapswithme.maps.MapStorage;
import com.mapswithme.maps.MapStorage.Index;
import com.mapswithme.maps.R;
import com.mapswithme.maps.guides.GuideInfo;
import com.mapswithme.util.UiUtils;
import com.mapswithme.util.Utils;
import com.mapswithme.util.statistics.Statistics;
/// ListView adapter
class DownloadAdapter extends BaseAdapter
{
/// @name Different row types.
//@{
private static final int TYPE_GROUP = 0;
private static final int TYPE_COUNTRY_GROUP = 1;
private static final int TYPE_COUNTRY_IN_PROCESS = 2;
private static final int TYPE_COUNTRY_READY = 3;
private static final int TYPE_COUNTRY_NOT_DOWNLOADED = 4;
private static final int TYPES_COUNT = 5;
//@}
private final LayoutInflater mInflater;
private final Activity mContext;
private int mSlotID = 0;
final MapStorage mStorage;
private final String mPackageName;
private static class CountryItem
{
public final String mName;
public final Index mCountryIdx;
public final String mFlag;
/// @see constants in MapStorage
public int mStatus;
public CountryItem(MapStorage storage, Index idx)
{
mCountryIdx = idx;
mName = storage.countryName(idx);
final String flag = storage.countryFlag(idx);
// The aapt can't process resources with name "do". Hack with renaming.
mFlag = flag.equals("do") ? "do_hack" : flag;
updateStatus(storage, idx);
}
public void updateStatus(MapStorage storage, Index idx)
{
if (idx.getCountry() == -1 || (idx.getRegion() == -1 && mFlag.length() == 0))
mStatus = MapStorage.GROUP;
else if (idx.getRegion() == -1 && storage.countriesCount(idx) > 0)
mStatus = MapStorage.COUNTRY;
else
mStatus = storage.countryStatus(idx);
}
public int getTextColor()
{
//TODO introduce resources
switch (mStatus)
{
case MapStorage.ON_DISK: return 0xFF00A144;
case MapStorage.ON_DISK_OUT_OF_DATE: return 0xFFFF69B4;
case MapStorage.NOT_DOWNLOADED: return 0xFF000000;
case MapStorage.DOWNLOAD_FAILED: return 0xFFFF0000;
case MapStorage.DOWNLOADING: return 0xFF342BB6;
case MapStorage.IN_QUEUE: return 0xFF5B94DE;
default: return 0xFF000000;
}
}
/// Get item type for list view representation;
public int getType()
{
switch (mStatus)
{
case MapStorage.GROUP: return TYPE_GROUP;
case MapStorage.COUNTRY: return TYPE_COUNTRY_GROUP;
case MapStorage.NOT_DOWNLOADED: return TYPE_COUNTRY_NOT_DOWNLOADED;
case MapStorage.ON_DISK:
case MapStorage.ON_DISK_OUT_OF_DATE:
return TYPE_COUNTRY_READY;
default : return TYPE_COUNTRY_IN_PROCESS;
}
}
}
private Index mIdx = new Index();
private DownloadAdapter.CountryItem[] mItems = null;
private final String m_kb;
private final String m_mb;
private final boolean mHasGoogleStore;
private final AlertDialog.Builder mAlert;
private final DialogInterface.OnClickListener m_alertCancelHandler =
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
{
dlg.dismiss();
}
};
public DownloadAdapter(Activity context)
{
final MWMApplication app = (MWMApplication) context.getApplication();
mStorage = app.getMapStorage();
mPackageName = app.getPackageName();
mContext = context;
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
m_kb = context.getString(R.string.kb);
m_mb = context.getString(R.string.mb);
mAlert = new AlertDialog.Builder(mContext);
mHasGoogleStore = Utils.hasAnyGoogleStoreInstalled();
fillList();
}
/// Fill list for current m_group and m_country.
private void fillList()
{
final int count = mStorage.countriesCount(mIdx);
if (count > 0)
{
mItems = new DownloadAdapter.CountryItem[count];
for (int i = 0; i < count; ++i)
mItems[i] = new CountryItem(mStorage, mIdx.getChild(i));
}
notifyDataSetChanged();
}
/// Process list item click.
public boolean onItemClick(int position)
{
if (mItems[position].mStatus < 0)
{
// expand next level
mIdx = mIdx.getChild(position);
fillList();
return true;
}
else
{
processCountry(position);
return false;
}
}
private void showNotEnoughFreeSpaceDialog(String spaceNeeded, String countryName)
{
new AlertDialog.Builder(mContext)
.setMessage(String.format(mContext.getString(R.string.free_space_for_country), spaceNeeded, countryName))
.setNegativeButton(mContext.getString(R.string.close), m_alertCancelHandler)
.create()
.show();
}
private boolean hasFreeSpace(long size)
{
final MWMApplication app = (MWMApplication) mContext.getApplication();
return app.hasFreeSpace(size);
}
private void processCountry(int position)
{
final Index idx = mIdx.getChild(position);
final String name = mItems[position].mName;
// Get actual status here
switch (mStorage.countryStatus(idx))
{
case MapStorage.ON_DISK:
// Confirm deleting
mAlert
.setTitle(name)
.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
{
mStorage.deleteCountry(idx);
Statistics.INSTANCE.trackCountryDeleted(mContext);
dlg.dismiss();
}
})
.setNegativeButton(R.string.cancel, m_alertCancelHandler)
.create()
.show();
break;
case MapStorage.ON_DISK_OUT_OF_DATE:
final long remoteSize = mStorage.countryRemoteSizeInBytes(idx);
// Update or delete
new AlertDialog.Builder(mContext)
.setTitle(name)
.setPositiveButton(mContext.getString(R.string.update_mb_or_kb, getSizeString(remoteSize)),
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
{
if (!hasFreeSpace(remoteSize + MB))
showNotEnoughFreeSpaceDialog(getSizeString(remoteSize), name);
else
{
mStorage.downloadCountry(idx);
Statistics.INSTANCE.trackCountryUpdate(mContext);
}
dlg.dismiss();
}
})
.setNeutralButton(R.string.delete, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
{
mStorage.deleteCountry(idx);
Statistics.INSTANCE.trackCountryDeleted(mContext);
dlg.dismiss();
}
})
.setNegativeButton(R.string.cancel, m_alertCancelHandler)
.create()
.show();
break;
case MapStorage.NOT_DOWNLOADED:
// Check for available free space
final long size = mStorage.countryRemoteSizeInBytes(idx);
if (!hasFreeSpace(size + MB))
{
showNotEnoughFreeSpaceDialog(getSizeString(size), name);
}
else
{
// Confirm downloading
mAlert
.setTitle(name)
.setPositiveButton(mContext.getString(R.string.download_mb_or_kb, getSizeString(size)),
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
{
mStorage.downloadCountry(idx);
Statistics.INSTANCE.trackCountryDownload(mContext);
dlg.dismiss();
}
})
.setNegativeButton(R.string.cancel, m_alertCancelHandler)
.create()
.show();
}
break;
case MapStorage.DOWNLOAD_FAILED:
// Do not confirm downloading if status is failed, just start it
mStorage.downloadCountry(idx);
break;
case MapStorage.DOWNLOADING:
// Confirm canceling
mAlert
.setTitle(name)
.setPositiveButton(R.string.cancel_download, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
{
mStorage.deleteCountry(idx);
dlg.dismiss();
}
})
.setNegativeButton(R.string.do_nothing, m_alertCancelHandler)
.create()
.show();
break;
case MapStorage.IN_QUEUE:
// Silently discard country from the queue
mStorage.deleteCountry(idx);
break;
}
// Actual status will be updated in "updateStatus" callback.
}
private void updateStatuses()
{
for (int i = 0; i < mItems.length; ++i)
{
final Index idx = mIdx.getChild(i);
assert(idx.isValid());
if (idx.isValid())
mItems[i].updateStatus(mStorage, idx);
}
}
/// @name Process routine from parent Activity.
//@{
/// @return true If "back" was processed.
public boolean onBackPressed()
{
// we are on the root level already - return
if (mIdx.isRoot())
return false;
// go to the parent level
mIdx = mIdx.getParent();
fillList();
return true;
}
public void onResume(MapStorage.Listener listener)
{
if (mSlotID == 0)
mSlotID = mStorage.subscribe(listener);
// update actual statuses for items after resuming activity
updateStatuses();
notifyDataSetChanged();
}
public void onPause()
{
if (mSlotID != 0)
{
mStorage.unsubscribe(mSlotID);
mSlotID = 0;
}
}
//@}
@Override
public int getItemViewType(int position)
{
return mItems[position].getType();
}
@Override
public int getViewTypeCount()
{
return TYPES_COUNT;
}
@Override
public int getCount()
{
return (mItems != null ? mItems.length : 0);
}
@Override
public DownloadAdapter.CountryItem getItem(int position)
{
return mItems[position];
}
@Override
public long getItemId(int position)
{
return position;
}
private static class ViewHolder
{
public TextView mName = null;
public ImageView mFlag = null;
public ImageView mGuide = null;
public ProgressBar mProgress = null;
public View mCountryMenu = null;
void initFromView(View v)
{
mName = (TextView) v.findViewById(R.id.title);
mFlag = (ImageView) v.findViewById(R.id.country_flag);
mGuide = (ImageView) v.findViewById(R.id.guide_available);
mCountryMenu = v.findViewById(R.id.country_menu);
mProgress = (ProgressBar) v.findViewById(R.id.download_progress);
}
}
/// Process "Map" button click in list view.
private class MapClickListener implements OnClickListener
{
private final int m_position;
public MapClickListener(int position) { m_position = position; }
@Override
public void onClick(View v)
{
mStorage.showCountry(mIdx.getChild(m_position));
// close parent activity
mContext.finish();
}
}
private String formatStringWithSize(int strID, int position)
{
return mContext.getString(strID, getSizeString(mStorage.countryLocalSizeInBytes(mIdx.getChild(position))));
}
private String getSummary(int position)
{
int res = 0;
switch (mItems[position].mStatus)
{
case MapStorage.ON_DISK:
return formatStringWithSize(R.string.downloaded_touch_to_delete, position);
case MapStorage.ON_DISK_OUT_OF_DATE:
return formatStringWithSize(R.string.downloaded_touch_to_update, position);
case MapStorage.NOT_DOWNLOADED: res = R.string.touch_to_download; break;
case MapStorage.DOWNLOAD_FAILED: res = R.string.download_has_failed; break;
case MapStorage.DOWNLOADING: res = R.string.downloading; break;
case MapStorage.IN_QUEUE: res = R.string.marked_for_downloading; break;
default:
return "An unknown error occured!";
}
return mContext.getString(res);
}
private void setFlag(int position, ImageView v)
{
final Resources res = mContext.getResources();
final String strID = mItems[position].mFlag;
final int id = res.getIdentifier(strID, "drawable", mPackageName);
if (id > 0)
v.setImageDrawable(res.getDrawable(id));
else
Log.e(DownloadUI.TAG, "Failed to get resource id from: " + strID);
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
DownloadAdapter.ViewHolder holder = null;
final int type = getItemViewType(position);
if (convertView == null)
{
holder = new ViewHolder();
switch (type)
{
case TYPE_GROUP:
convertView = mInflater.inflate(R.layout.download_item_group, null);
holder.initFromView(convertView);
break;
case TYPE_COUNTRY_GROUP:
convertView = mInflater.inflate(R.layout.download_item_country_group, null);
holder.initFromView(convertView);
break;
case TYPE_COUNTRY_IN_PROCESS:
case TYPE_COUNTRY_READY:
case TYPE_COUNTRY_NOT_DOWNLOADED:
convertView = mInflater.inflate(R.layout.download_item_country, null);
holder.initFromView(convertView);
break;
}
convertView.setTag(holder);
}
else
{
holder = (DownloadAdapter.ViewHolder) convertView.getTag();
}
// for everything that has flag: regions + countries
if (type != TYPE_GROUP)
{
setFlag(position, holder.mFlag);
// this part if only for downloadable items
if (type != TYPE_COUNTRY_GROUP)
{
populateForGuide(position, holder);
if (type == TYPE_COUNTRY_IN_PROCESS)
{
holder.mProgress.setProgress(0);
UiUtils.show(holder.mProgress);
}
else
UiUtils.hide(holder.mProgress);
}
}
setItemText(position, holder);
return convertView;
}
private void setItemText(int position, DownloadAdapter.ViewHolder holder)
{
// set texts
holder.mName.setText(mItems[position].mName);
holder.mName.setTextColor(mItems[position].getTextColor());
}
private void populateForGuide(int position, DownloadAdapter.ViewHolder holder)
{
if (mHasGoogleStore)
{
final CountryItem item = getItem(position);
final GuideInfo gi = Framework.getGuideInfoForIndex(item.mCountryIdx);
if (gi != null)
UiUtils.show(holder.mGuide);
else
UiUtils.hide(holder.mGuide);
}
}
/// Get list item position by index(g, c, r).
/// @return -1 If no such item in display list.
private int getItemPosition(Index idx)
{
if (mIdx.isChild(idx))
{
final int position = idx.getPosition();
if (position >= 0 && position < mItems.length)
return position;
else
Log.e(DownloadUI.TAG, "Incorrect item position for: " + idx.toString());
}
return -1;
}
/// @return Current country status (@see MapStorage).
public int onCountryStatusChanged(Index idx)
{
final int position = getItemPosition(idx);
if (position != -1)
{
mItems[position].updateStatus(mStorage, idx);
// use this hard reset, because of caching different ViewHolders according to item's type
notifyDataSetChanged();
return mItems[position].mStatus;
}
return MapStorage.UNKNOWN;
}
public void onCountryProgress(ListView list, Index idx, long current, long total)
{
final int position = getItemPosition(idx);
if (position != -1)
{
assert(mItems[position].mStatus == 3);
// do update only one item's view; don't call notifyDataSetChanged
final View v = list.getChildAt(position - list.getFirstVisiblePosition());
if (v != null)
{
final DownloadAdapter.ViewHolder holder = (DownloadAdapter.ViewHolder) v.getTag();
holder.mProgress.setProgress((int) (current*100/total));
}
}
}
private String getSizeString(long size)
{
if (size > MB)
{
// do the correct rounding of MB
return (size + 512 * 1024) / MB + " " + m_mb;
}
else
{
// get upper bound size for Kb
return (size + 1023) / 1024 + " " + m_kb;
}
}
private final static long MB = 1024 * 1024;
}

View file

@ -0,0 +1,156 @@
package com.mapswithme.country;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
import com.mapswithme.maps.ContextMenu;
import com.mapswithme.maps.MWMActivity;
import com.mapswithme.maps.MapStorage;
import com.mapswithme.maps.MapStorage.Index;
import com.mapswithme.maps.R;
import com.mapswithme.maps.base.MapsWithMeBaseListActivity;
import com.mapswithme.util.ConnectionState;
public class DownloadUI extends MapsWithMeBaseListActivity implements MapStorage.Listener
{
static String TAG = "DownloadUI";
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.downloader_list_view);
setListAdapter(new DownloadAdapter(this));
}
private DownloadAdapter getDA()
{
return (DownloadAdapter) getListView().getAdapter();
}
@Override
protected void onResume()
{
super.onResume();
getDA().onResume(this);
}
@Override
protected void onPause()
{
super.onPause();
getDA().onPause();
}
@Override
public void onBackPressed()
{
if (getDA().onBackPressed())
{
// scroll list view to the top
setSelection(0);
}
else
{
super.onBackPressed();
// Always show map as parent
startActivity(new Intent(this, MWMActivity.class));
}
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id)
{
super.onListItemClick(l, v, position, id);
if (getDA().onItemClick(position))
{
// scroll list view to the top
setSelection(0);
}
}
@Override
public void onCountryStatusChanged(final Index idx)
{
if (getDA().onCountryStatusChanged(idx) == MapStorage.DOWNLOAD_FAILED)
{
// Show wireless settings page if no connection found.
if (ConnectionState.getState(this) == ConnectionState.NOT_CONNECTED)
{
final DownloadUI activity = this;
final String country = getDA().mStorage.countryName(idx);
runOnUiThread(new Runnable()
{
@Override
public void run()
{
new AlertDialog.Builder(activity)
.setCancelable(false)
.setMessage(String.format(getString(R.string.download_country_failed), country))
.setPositiveButton(getString(R.string.connection_settings), new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
{
try
{
startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
}
catch (final Exception ex)
{
Log.e(TAG, "Can't run activity:" + ex);
}
dlg.dismiss();
}
})
.setNegativeButton(getString(R.string.close), new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
{
dlg.dismiss();
}
})
.create()
.show();
}
});
}
}
}
@Override
public void onCountryProgress(Index idx, long current, long total)
{
getDA().onCountryProgress(getListView(), idx, current, total);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
return ContextMenu.onCreateOptionsMenu(this, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
if (ContextMenu.onOptionsItemSelected(this, item))
return true;
else
return super.onOptionsItemSelected(item);
}
}

View file

@ -1,789 +0,0 @@
package com.mapswithme.maps;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import com.mapswithme.maps.MapStorage.Index;
import com.mapswithme.maps.base.MapsWithMeBaseListActivity;
import com.mapswithme.maps.guides.GuideInfo;
import com.mapswithme.maps.guides.GuidesUtils;
import com.mapswithme.util.ConnectionState;
import com.mapswithme.util.UiUtils;
import com.mapswithme.util.Utils;
import com.mapswithme.util.statistics.Statistics;
public class DownloadUI extends MapsWithMeBaseListActivity implements MapStorage.Listener
{
private static String TAG = "DownloadUI";
/// ListView adapter
private static class DownloadAdapter extends BaseAdapter
{
/// @name Different row types.
//@{
private static final int TYPE_GROUP = 0;
private static final int TYPE_COUNTRY_GROUP = 1;
private static final int TYPE_COUNTRY_IN_PROCESS = 2;
private static final int TYPE_COUNTRY_READY = 3;
private static final int TYPES_COUNT = 4;
//@}
private final LayoutInflater m_inflater;
private final Activity m_context;
private int m_slotID = 0;
private final MapStorage m_storage;
private final String m_packageName;
private static class CountryItem
{
public final String m_name;
public final Index mIdx;
public String m_flag;
/// @see constants in MapStorage
public int m_status;
public CountryItem(MapStorage storage, Index idx)
{
mIdx = idx;
m_name = storage.countryName(idx);
m_flag = storage.countryFlag(idx);
// The aapt can't process resources with name "do". Hack with renaming.
if (m_flag.equals("do"))
m_flag = "do_hack";
updateStatus(storage, idx);
}
public void updateStatus(MapStorage storage, Index idx)
{
if (idx.mCountry == -1 || (idx.mRegion == -1 && m_flag.length() == 0))
{
// group and not a country
m_status = MapStorage.GROUP;
}
else if (idx.mRegion == -1 && storage.countriesCount(idx) > 0)
{
// country with grouping
m_status = MapStorage.COUNTRY;
}
else
{
// country or region without grouping
m_status = storage.countryStatus(idx);
}
}
public int getTextColor()
{
//TODO introduce resources
switch (m_status)
{
case MapStorage.ON_DISK: return 0xFF00A144;
case MapStorage.ON_DISK_OUT_OF_DATE: return 0xFFFF69B4;
case MapStorage.NOT_DOWNLOADED: return 0xFF000000;
case MapStorage.DOWNLOAD_FAILED: return 0xFFFF0000;
case MapStorage.DOWNLOADING: return 0xFF342BB6;
case MapStorage.IN_QUEUE: return 0xFF5B94DE;
default: return 0xFF000000;
}
}
/// Get item type for list view representation;
public int getType()
{
switch (m_status)
{
case MapStorage.GROUP: return TYPE_GROUP;
case MapStorage.COUNTRY: return TYPE_COUNTRY_GROUP;
case MapStorage.ON_DISK:
case MapStorage.ON_DISK_OUT_OF_DATE:
return TYPE_COUNTRY_READY;
default : return TYPE_COUNTRY_IN_PROCESS;
}
}
}
private Index m_idx = new Index();
private CountryItem[] m_items = null;
private final String m_kb;
private final String m_mb;
private final boolean mHasGoogleStore;
private final AlertDialog.Builder m_alert;
private final DialogInterface.OnClickListener m_alertCancelHandler =
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
{
dlg.dismiss();
}
};
public DownloadAdapter(Activity context)
{
final MWMApplication app = (MWMApplication) context.getApplication();
m_storage = app.getMapStorage();
m_packageName = app.getPackageName();
m_context = context;
m_inflater = (LayoutInflater) m_context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
m_kb = context.getString(R.string.kb);
m_mb = context.getString(R.string.mb);
m_alert = new AlertDialog.Builder(m_context);
mHasGoogleStore = Utils.hasAnyGoogleStoreInstalled();
fillList();
}
private final long MB = 1024 * 1024;
private String getSizeString(long size)
{
if (size > MB)
{
// do the correct rounding of MB
return (size + 512 * 1024) / MB + " " + m_mb;
}
else
{
// get upper bound size for Kb
return (size + 1023) / 1024 + " " + m_kb;
}
}
/// Fill list for current m_group and m_country.
private void fillList()
{
final int count = m_storage.countriesCount(m_idx);
if (count > 0)
{
m_items = new CountryItem[count];
for (int i = 0; i < count; ++i)
m_items[i] = new CountryItem(m_storage, m_idx.getChild(i));
}
notifyDataSetChanged();
}
/// Process list item click.
public boolean onItemClick(int position)
{
if (m_items[position].m_status < 0)
{
// expand next level
m_idx = m_idx.getChild(position);
fillList();
return true;
}
else
{
processCountry(position);
return false;
}
}
private void showNotEnoughFreeSpaceDialog(String spaceNeeded, String countryName)
{
new AlertDialog.Builder(m_context)
.setMessage(String.format(m_context.getString(R.string.free_space_for_country), spaceNeeded, countryName))
.setNegativeButton(m_context.getString(R.string.close), m_alertCancelHandler)
.create()
.show();
}
private boolean hasFreeSpace(long size)
{
final MWMApplication app = (MWMApplication) m_context.getApplication();
return app.hasFreeSpace(size);
}
private void processCountry(int position)
{
final Index idx = m_idx.getChild(position);
final String name = m_items[position].m_name;
// Get actual status here
switch (m_storage.countryStatus(idx))
{
case MapStorage.ON_DISK:
// Confirm deleting
m_alert
.setTitle(name)
.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
{
m_storage.deleteCountry(idx);
Statistics.INSTANCE.trackCountryDeleted(m_context);
dlg.dismiss();
}
})
.setNegativeButton(R.string.cancel, m_alertCancelHandler)
.create()
.show();
break;
case MapStorage.ON_DISK_OUT_OF_DATE:
final long remoteSize = m_storage.countryRemoteSizeInBytes(idx);
// Update or delete
new AlertDialog.Builder(m_context)
.setTitle(name)
.setPositiveButton(m_context.getString(R.string.update_mb_or_kb, getSizeString(remoteSize)),
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
{
if (!hasFreeSpace(remoteSize + MB))
showNotEnoughFreeSpaceDialog(getSizeString(remoteSize), name);
else
{
m_storage.downloadCountry(idx);
Statistics.INSTANCE.trackCountryUpdate(m_context);
}
dlg.dismiss();
}
})
.setNeutralButton(R.string.delete, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
{
m_storage.deleteCountry(idx);
Statistics.INSTANCE.trackCountryDeleted(m_context);
dlg.dismiss();
}
})
.setNegativeButton(R.string.cancel, m_alertCancelHandler)
.create()
.show();
break;
case MapStorage.NOT_DOWNLOADED:
// Check for available free space
final long size = m_storage.countryRemoteSizeInBytes(idx);
if (!hasFreeSpace(size + MB))
{
showNotEnoughFreeSpaceDialog(getSizeString(size), name);
}
else
{
// Confirm downloading
m_alert
.setTitle(name)
.setPositiveButton(m_context.getString(R.string.download_mb_or_kb, getSizeString(size)),
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
{
m_storage.downloadCountry(idx);
Statistics.INSTANCE.trackCountryDownload(m_context);
dlg.dismiss();
}
})
.setNegativeButton(R.string.cancel, m_alertCancelHandler)
.create()
.show();
}
break;
case MapStorage.DOWNLOAD_FAILED:
// Do not confirm downloading if status is failed, just start it
m_storage.downloadCountry(idx);
break;
case MapStorage.DOWNLOADING:
// Confirm canceling
m_alert
.setTitle(name)
.setPositiveButton(R.string.cancel_download, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
{
m_storage.deleteCountry(idx);
dlg.dismiss();
}
})
.setNegativeButton(R.string.do_nothing, m_alertCancelHandler)
.create()
.show();
break;
case MapStorage.IN_QUEUE:
// Silently discard country from the queue
m_storage.deleteCountry(idx);
break;
}
// Actual status will be updated in "updateStatus" callback.
}
private void updateStatuses()
{
for (int i = 0; i < m_items.length; ++i)
{
final Index idx = m_idx.getChild(i);
assert(idx.isValid());
if (idx.isValid())
m_items[i].updateStatus(m_storage, idx);
}
}
/// @name Process routine from parent Activity.
//@{
/// @return true If "back" was processed.
public boolean onBackPressed()
{
// we are on the root level already - return
if (m_idx.isRoot())
return false;
// go to the parent level
m_idx = m_idx.getParent();
fillList();
return true;
}
public void onResume(MapStorage.Listener listener)
{
if (m_slotID == 0)
m_slotID = m_storage.subscribe(listener);
// update actual statuses for items after resuming activity
updateStatuses();
notifyDataSetChanged();
}
public void onPause()
{
if (m_slotID != 0)
{
m_storage.unsubscribe(m_slotID);
m_slotID = 0;
}
}
//@}
@Override
public int getItemViewType(int position)
{
return m_items[position].getType();
}
@Override
public int getViewTypeCount()
{
return TYPES_COUNT;
}
@Override
public int getCount()
{
return (m_items != null ? m_items.length : 0);
}
@Override
public CountryItem getItem(int position)
{
return m_items[position];
}
@Override
public long getItemId(int position)
{
return position;
}
private static class ViewHolder
{
public TextView mName = null;
public TextView mSummary = null;
public ImageView mFlag = null;
public ImageView mMap = null;
public ImageView mGuide = null;
void initFromView(View v)
{
mName = (TextView) v.findViewById(R.id.title);
mSummary = (TextView) v.findViewById(R.id.summary);
mFlag = (ImageView) v.findViewById(R.id.country_flag);
mMap = (ImageView) v.findViewById(R.id.show_country);
mGuide = (ImageView) v.findViewById(R.id.guide_available);
}
}
/// Process "Map" button click in list view.
private class MapClickListener implements OnClickListener
{
private final int m_position;
public MapClickListener(int position) { m_position = position; }
@Override
public void onClick(View v)
{
m_storage.showCountry(m_idx.getChild(m_position));
// close parent activity
m_context.finish();
}
}
private String formatStringWithSize(int strID, int position)
{
return m_context.getString(strID, getSizeString(m_storage.countryLocalSizeInBytes(m_idx.getChild(position))));
}
private String getSummary(int position)
{
int res = 0;
switch (m_items[position].m_status)
{
case MapStorage.ON_DISK:
return formatStringWithSize(R.string.downloaded_touch_to_delete, position);
case MapStorage.ON_DISK_OUT_OF_DATE:
return formatStringWithSize(R.string.downloaded_touch_to_update, position);
case MapStorage.NOT_DOWNLOADED: res = R.string.touch_to_download; break;
case MapStorage.DOWNLOAD_FAILED: res = R.string.download_has_failed; break;
case MapStorage.DOWNLOADING: res = R.string.downloading; break;
case MapStorage.IN_QUEUE: res = R.string.marked_for_downloading; break;
default:
return "An unknown error occured!";
}
return m_context.getString(res);
}
private void setFlag(int position, ImageView v)
{
final Resources res = m_context.getResources();
final String strID = m_items[position].m_flag;
final int id = res.getIdentifier(strID, "drawable", m_packageName);
if (id > 0)
v.setImageDrawable(res.getDrawable(id));
else
Log.e(TAG, "Failed to get resource id from: " + strID);
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder holder = null;
if (convertView == null)
{
holder = new ViewHolder();
switch (getItemViewType(position))
{
case TYPE_GROUP:
convertView = m_inflater.inflate(R.layout.download_item_group, null);
holder.initFromView(convertView);
break;
case TYPE_COUNTRY_GROUP:
convertView = m_inflater.inflate(R.layout.download_item_country_group, null);
holder.initFromView(convertView);
break;
case TYPE_COUNTRY_IN_PROCESS:
convertView = m_inflater.inflate(R.layout.download_item_country, null);
holder.initFromView(convertView);
holder.mMap.setVisibility(Button.INVISIBLE);
break;
case TYPE_COUNTRY_READY:
convertView = m_inflater.inflate(R.layout.download_item_country, null);
holder.initFromView(convertView);
break;
}
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
// set texts
holder.mName.setText(m_items[position].m_name);
holder.mName.setTextColor(m_items[position].getTextColor());
if (holder.mSummary != null)
holder.mSummary.setText(getSummary(position));
// attach to "Map" button if needed
if (holder.mMap != null && holder.mMap.getVisibility() == Button.VISIBLE)
holder.mMap.setOnClickListener(new MapClickListener(position));
// flag or guide
if (holder.mFlag != null)
{
if (holder.mGuide != null)
UiUtils.hide(holder.mGuide);
UiUtils.show(holder.mFlag);
setFlag(position, holder.mFlag);
}
if (mHasGoogleStore)
{
final CountryItem item = getItem(position);
if (item.getType() == TYPE_COUNTRY_IN_PROCESS || item.getType() == TYPE_COUNTRY_READY)
{
final GuideInfo gi = Framework.getGuideInfoForIndex(item.mIdx);
if (gi != null)
{
UiUtils.hide(holder.mFlag);
UiUtils.show(holder.mGuide);
holder.mGuide.setImageResource(R.drawable.ic_guide);
holder.mGuide.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
final String packName = gi.mAppId;
if (GuidesUtils.isGuideInstalled(packName, m_context))
{
final Intent i = m_context.getPackageManager().getLaunchIntentForPackage(packName);
m_context.startActivity(i);
}
else
m_context.startActivity(GuidesUtils.getGoogleStoreIntentForPackage(gi.mAppId));
}
});
}
}
}
return convertView;
}
/// Get list item position by index(g, c, r).
/// @return -1 If no such item in display list.
private int getItemPosition(Index idx)
{
if (m_idx.isChild(idx))
{
final int position = idx.getPosition();
if (position >= 0 && position < m_items.length)
return position;
else
Log.e(TAG, "Incorrect item position for: " + idx.toString());
}
return -1;
}
/// @return Current country status (@see MapStorage).
public int onCountryStatusChanged(Index idx)
{
final int position = getItemPosition(idx);
if (position != -1)
{
m_items[position].updateStatus(m_storage, idx);
// use this hard reset, because of caching different ViewHolders according to item's type
notifyDataSetChanged();
return m_items[position].m_status;
}
return MapStorage.UNKNOWN;
}
public void onCountryProgress(ListView list, Index idx, long current, long total)
{
final int position = getItemPosition(idx);
if (position != -1)
{
assert(m_items[position].m_status == 3);
// do update only one item's view; don't call notifyDataSetChanged
final View v = list.getChildAt(position - list.getFirstVisiblePosition());
if (v != null)
{
final ViewHolder holder = (ViewHolder) v.getTag();
// This function actually is a callback from downloading engine and
// when using cached views and holders, summary may be null
// because of different ListView context.
if (holder != null && holder.mSummary != null)
{
holder.mSummary.setText(String.format(m_context.getString(R.string.downloading_touch_to_cancel),
current * 100 / total));
v.invalidate();
}
}
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.downloader_list_view);
setListAdapter(new DownloadAdapter(this));
}
private DownloadAdapter getDA()
{
return (DownloadAdapter) getListView().getAdapter();
}
@Override
protected void onResume()
{
super.onResume();
getDA().onResume(this);
}
@Override
protected void onPause()
{
super.onPause();
getDA().onPause();
}
@Override
public void onBackPressed()
{
if (getDA().onBackPressed())
{
// scroll list view to the top
setSelection(0);
}
else
{
super.onBackPressed();
// Always show map as parent
startActivity(new Intent(this, MWMActivity.class));
}
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id)
{
super.onListItemClick(l, v, position, id);
if (getDA().onItemClick(position))
{
// scroll list view to the top
setSelection(0);
}
}
@Override
public void onCountryStatusChanged(final Index idx)
{
if (getDA().onCountryStatusChanged(idx) == MapStorage.DOWNLOAD_FAILED)
{
// Show wireless settings page if no connection found.
if (ConnectionState.getState(this) == ConnectionState.NOT_CONNECTED)
{
final DownloadUI activity = this;
final String country = getDA().m_storage.countryName(idx);
runOnUiThread(new Runnable()
{
@Override
public void run()
{
new AlertDialog.Builder(activity)
.setCancelable(false)
.setMessage(String.format(getString(R.string.download_country_failed), country))
.setPositiveButton(getString(R.string.connection_settings), new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
{
try
{
startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
}
catch (final Exception ex)
{
Log.e(TAG, "Can't run activity:" + ex);
}
dlg.dismiss();
}
})
.setNegativeButton(getString(R.string.close), new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
{
dlg.dismiss();
}
})
.create()
.show();
}
});
}
}
}
@Override
public void onCountryProgress(Index idx, long current, long total)
{
getDA().onCountryProgress(getListView(), idx, current, total);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
return ContextMenu.onCreateOptionsMenu(this, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
if (ContextMenu.onOptionsItemSelected(this, item))
return true;
else
return super.onOptionsItemSelected(item);
}
}

View file

@ -34,6 +34,7 @@ import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.mapswithme.country.DownloadUI;
import com.mapswithme.maps.Framework.OnBalloonListener;
import com.mapswithme.maps.MapStorage.Index;
import com.mapswithme.maps.api.ParsedMmwRequest;

View file

@ -31,26 +31,26 @@ public class MapStorage
private static final long serialVersionUID = 1L;
int mGroup;
int mCountry;
int mRegion;
private int mCountry;
private int mRegion;
public Index()
{
mGroup = -1;
mCountry = -1;
mRegion = -1;
setCountry(-1);
setRegion(-1);
}
public Index(int group, int country, int region)
{
mGroup = group;
mCountry = country;
mRegion = region;
setCountry(country);
setRegion(region);
}
public boolean isRoot()
{
return (mGroup == -1 && mCountry == -1 && mRegion == -1);
return (mGroup == -1 && getCountry() == -1 && getRegion() == -1);
}
public boolean isValid()
@ -60,14 +60,14 @@ public class MapStorage
public Index getChild(int position)
{
final Index ret = new Index(mGroup, mCountry, mRegion);
final Index ret = new Index(mGroup, getCountry(), getRegion());
if (ret.mGroup == -1) ret.mGroup = position;
else if (ret.mCountry == -1) ret.mCountry = position;
else if (ret.getCountry() == -1) ret.setCountry(position);
else
{
assert(ret.mRegion == -1);
ret.mRegion = position;
assert(ret.getRegion() == -1);
ret.setRegion(position);
}
return ret;
@ -75,10 +75,10 @@ public class MapStorage
public Index getParent()
{
final Index ret = new Index(mGroup, mCountry, mRegion);
final Index ret = new Index(mGroup, getCountry(), getRegion());
if (ret.mRegion != -1) ret.mRegion = -1;
else if (ret.mCountry != -1) ret.mCountry = -1;
if (ret.getRegion() != -1) ret.setRegion(-1);
else if (ret.getCountry() != -1) ret.setCountry(-1);
else
{
assert(ret.mGroup != -1);
@ -90,7 +90,7 @@ public class MapStorage
public boolean isEqual(Index idx)
{
return (mGroup == idx.mGroup && mCountry == idx.mCountry && mRegion == idx.mRegion);
return (mGroup == idx.mGroup && getCountry() == idx.getCountry() && getRegion() == idx.getRegion());
}
public boolean isChild(Index idx)
@ -100,15 +100,35 @@ public class MapStorage
public int getPosition()
{
if (mRegion != -1) return mRegion;
else if (mCountry != -1) return mCountry;
if (getRegion() != -1) return getRegion();
else if (getCountry() != -1) return getCountry();
else return mGroup;
}
@Override
public String toString()
{
return ("Index(" + mGroup + ", " + mCountry + ", " + mRegion + ")");
return ("Index(" + mGroup + ", " + getCountry() + ", " + getRegion() + ")");
}
public int getCountry()
{
return mCountry;
}
public void setCountry(int mCountry)
{
this.mCountry = mCountry;
}
public int getRegion()
{
return mRegion;
}
public void setRegion(int mRegion)
{
this.mRegion = mRegion;
}
}

View file

@ -7,7 +7,7 @@ import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import com.mapswithme.maps.DownloadUI;
import com.mapswithme.country.DownloadUI;
import com.mapswithme.maps.MWMActivity;
import com.mapswithme.maps.MapStorage.Index;
import com.mapswithme.maps.R;

View file

@ -1,5 +1,6 @@
package com.mapswithme.maps.guides;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@ -30,4 +31,16 @@ public class GuidesUtils
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET | Intent.FLAG_ACTIVITY_NO_HISTORY);
return intent;
}
public static void openOrDownloadGuide(GuideInfo info, Activity activity)
{
final String packName = info.mAppId;
if (GuidesUtils.isGuideInstalled(packName, activity))
{
final Intent i = activity.getPackageManager().getLaunchIntentForPackage(packName);
activity.startActivity(i);
}
else
activity.startActivity(GuidesUtils.getGoogleStoreIntentForPackage(info.mAppId));
}
}