Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Yunitsky <yunik@mapswithme.com>2015-09-10 12:32:18 +0300
committerAlex Zolotarev <alex@maps.me>2015-09-23 03:04:30 +0300
commit1de8221d8c8676e40df52992fdb7e1a68acaffc2 (patch)
treef6ebfd999689079b2a93adaa772f772ab9edd2ba /android
parent9f4bb161a9d80b102863f66480cd48ad7a99afd7 (diff)
[android] Changed internal map model refresh order. Fixed crash after opening map from DownloadResourcesActivity when map download is active.
Diffstat (limited to 'android')
-rw-r--r--android/UnitTests/jni/mock.cpp5
-rw-r--r--android/jni/com/mapswithme/maps/Framework.cpp12
-rw-r--r--android/jni/com/mapswithme/maps/MapFragment.cpp14
-rw-r--r--android/jni/com/mapswithme/maps/MapStorage.cpp4
-rw-r--r--android/jni/com/mapswithme/maps/MwmApplication.cpp7
-rw-r--r--android/jni/com/mapswithme/platform/Platform.cpp4
-rw-r--r--android/jni/com/mapswithme/platform/Platform.hpp2
-rw-r--r--android/src/com/mapswithme/maps/DownloadResourcesActivity.java4
-rw-r--r--android/src/com/mapswithme/maps/Framework.java7
-rw-r--r--android/src/com/mapswithme/maps/MapFragment.java4
-rw-r--r--android/src/com/mapswithme/maps/MapStorage.java25
-rw-r--r--android/src/com/mapswithme/maps/MwmActivity.java193
-rw-r--r--android/src/com/mapswithme/maps/settings/StorageItem.java52
-rw-r--r--android/src/com/mapswithme/maps/settings/StoragePathAdapter.java34
-rw-r--r--android/src/com/mapswithme/maps/settings/StoragePathFragment.java29
-rw-r--r--android/src/com/mapswithme/maps/settings/StoragePathManager.java594
-rw-r--r--android/src/com/mapswithme/maps/settings/StorageUtils.java297
17 files changed, 611 insertions, 676 deletions
diff --git a/android/UnitTests/jni/mock.cpp b/android/UnitTests/jni/mock.cpp
index beb0a44803..acac3a88eb 100644
--- a/android/UnitTests/jni/mock.cpp
+++ b/android/UnitTests/jni/mock.cpp
@@ -179,11 +179,6 @@ namespace android_tests
m_androidDefResScope = "rfw";
}
- void OnExternalStorageStatusChanged(bool isAvailable)
- {
- LOG(LWARNING, ("OnExternalStorageStatusChanged() is not implemented."));
- }
-
/// get storage path without ending "/MapsWithMe/"
string GetStoragePathPrefix() const
{
diff --git a/android/jni/com/mapswithme/maps/Framework.cpp b/android/jni/com/mapswithme/maps/Framework.cpp
index 7e9eb9177a..7656f4d644 100644
--- a/android/jni/com/mapswithme/maps/Framework.cpp
+++ b/android/jni/com/mapswithme/maps/Framework.cpp
@@ -1567,4 +1567,16 @@ extern "C"
m2::PointD const pivot = m2::PointD(pivotX, pivotY);
android::Platform::RunOnGuiThreadImpl(bind(&Framework::SetWidgetPivot, frm(), widgetType, pivot));
}
+
+ JNIEXPORT void JNICALL
+ Java_com_mapswithme_maps_Framework_nativeRegisterMaps(JNIEnv * env, jclass thiz)
+ {
+ frm()->RegisterAllMaps();
+ }
+
+ JNIEXPORT void JNICALL
+ Java_com_mapswithme_maps_Framework_nativeDeregisterMaps(JNIEnv * env, jclass thiz)
+ {
+ frm()->DeregisterAllMaps();
+ }
} // extern "C"
diff --git a/android/jni/com/mapswithme/maps/MapFragment.cpp b/android/jni/com/mapswithme/maps/MapFragment.cpp
index 998bdea790..420aea8dbd 100644
--- a/android/jni/com/mapswithme/maps/MapFragment.cpp
+++ b/android/jni/com/mapswithme/maps/MapFragment.cpp
@@ -128,20 +128,6 @@ extern "C"
}
JNIEXPORT void JNICALL
- Java_com_mapswithme_maps_MapFragment_nativeStorageConnected(JNIEnv * env, jobject thiz)
- {
- android::Platform::Instance().OnExternalStorageStatusChanged(true);
- g_framework->AddLocalMaps();
- }
-
- JNIEXPORT void JNICALL
- Java_com_mapswithme_maps_MapFragment_nativeStorageDisconnected(JNIEnv * env, jobject thiz)
- {
- android::Platform::Instance().OnExternalStorageStatusChanged(false);
- g_framework->RemoveLocalMaps();
- }
-
- JNIEXPORT void JNICALL
Java_com_mapswithme_maps_MapFragment_nativeScale(JNIEnv * env, jobject thiz, jdouble k)
{
g_framework->Scale(static_cast<double>(k));
diff --git a/android/jni/com/mapswithme/maps/MapStorage.cpp b/android/jni/com/mapswithme/maps/MapStorage.cpp
index 97afe451f2..255f14c6a8 100644
--- a/android/jni/com/mapswithme/maps/MapStorage.cpp
+++ b/android/jni/com/mapswithme/maps/MapStorage.cpp
@@ -1,7 +1,7 @@
#include "MapStorage.hpp"
#include "Framework.hpp"
-
-#include "../country/country_helper.hpp"
+#include "com/mapswithme/country/country_helper.hpp"
+#include "com/mapswithme/platform/Platform.hpp"
#include "coding/internal/file_data.hpp"
diff --git a/android/jni/com/mapswithme/maps/MwmApplication.cpp b/android/jni/com/mapswithme/maps/MwmApplication.cpp
index ee3864c6a2..42f91ad9d3 100644
--- a/android/jni/com/mapswithme/maps/MwmApplication.cpp
+++ b/android/jni/com/mapswithme/maps/MwmApplication.cpp
@@ -1,10 +1,3 @@
-/*
- * MWMService.cpp
- *
- * Created on: May 11, 2012
- * Author: siarheirachytski
- */
-
#include "Framework.hpp"
#include "../core/jni_helper.hpp"
diff --git a/android/jni/com/mapswithme/platform/Platform.cpp b/android/jni/com/mapswithme/platform/Platform.cpp
index 849e729e9b..661935d7a8 100644
--- a/android/jni/com/mapswithme/platform/Platform.cpp
+++ b/android/jni/com/mapswithme/platform/Platform.cpp
@@ -150,10 +150,6 @@ namespace android
(void) ConnectionStatus();
}
- void Platform::OnExternalStorageStatusChanged(bool isAvailable)
- {
- }
-
string Platform::GetStoragePathPrefix() const
{
size_t const count = m_writableDir.size();
diff --git a/android/jni/com/mapswithme/platform/Platform.hpp b/android/jni/com/mapswithme/platform/Platform.hpp
index bd5ff8bde0..63d39ec52c 100644
--- a/android/jni/com/mapswithme/platform/Platform.hpp
+++ b/android/jni/com/mapswithme/platform/Platform.hpp
@@ -16,8 +16,6 @@ namespace android
jstring flavorName, jstring buildType,
bool isYota, bool isTablet);
- void OnExternalStorageStatusChanged(bool isAvailable);
-
/// get storage path without ending "/MapsWithMe/"
string GetStoragePathPrefix() const;
/// assign storage path (should contain ending "/MapsWithMe/")
diff --git a/android/src/com/mapswithme/maps/DownloadResourcesActivity.java b/android/src/com/mapswithme/maps/DownloadResourcesActivity.java
index b02d06e9c5..cf7be32a13 100644
--- a/android/src/com/mapswithme/maps/DownloadResourcesActivity.java
+++ b/android/src/com/mapswithme/maps/DownloadResourcesActivity.java
@@ -421,6 +421,10 @@ public class DownloadResourcesActivity extends BaseMwmFragmentActivity
{
if (result == ERR_NO_MORE_FILES)
{
+ // World and WorldCoasts has been downloaded, we should register maps again to correctly add them to the model and generate indexes etc.
+ // TODO fix the hack when separate download of World-s will be removed or refactored
+ Framework.nativeDeregisterMaps();
+ Framework.nativeRegisterMaps();
if (mCountryIndex != null && mChbDownloadCountry.isChecked())
{
UiUtils.hide(mChbDownloadCountry, mTvLocation);
diff --git a/android/src/com/mapswithme/maps/Framework.java b/android/src/com/mapswithme/maps/Framework.java
index c8e19e4d48..6ec47c89ba 100644
--- a/android/src/com/mapswithme/maps/Framework.java
+++ b/android/src/com/mapswithme/maps/Framework.java
@@ -181,4 +181,11 @@ public class Framework
public native static int nativeGetBestRouter(double srcLat, double srcLon, double dstLat, double dstLon);
public native static void setWidgetPivot(int widget, int pivotX, int pivotY);
+
+ /**
+ * Registers all maps(.mwms). Adds them to the models, generates indexes and does all necessary stuff.
+ */
+ public native static void nativeRegisterMaps();
+
+ public native static void nativeDeregisterMaps();
}
diff --git a/android/src/com/mapswithme/maps/MapFragment.java b/android/src/com/mapswithme/maps/MapFragment.java
index 9a1c4ab353..d315e12117 100644
--- a/android/src/com/mapswithme/maps/MapFragment.java
+++ b/android/src/com/mapswithme/maps/MapFragment.java
@@ -21,10 +21,6 @@ public class MapFragment extends NvEventQueueFragment
public static final String FRAGMENT_TAG = MapFragment.class.getSimpleName();
- protected native void nativeStorageConnected();
-
- protected native void nativeStorageDisconnected();
-
protected native void nativeConnectDownloadButton();
protected native void nativeDownloadCountry(MapStorage.Index index, int options);
diff --git a/android/src/com/mapswithme/maps/MapStorage.java b/android/src/com/mapswithme/maps/MapStorage.java
index b70b53216f..0559133703 100644
--- a/android/src/com/mapswithme/maps/MapStorage.java
+++ b/android/src/com/mapswithme/maps/MapStorage.java
@@ -1,11 +1,18 @@
package com.mapswithme.maps;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Environment;
import android.support.annotation.StringRes;
import android.support.v7.app.AlertDialog;
+import com.mapswithme.maps.settings.StoragePathManager;
+
import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
public enum MapStorage
{
@@ -34,6 +41,13 @@ public enum MapStorage
void onCountryProgress(Index idx, long current, long total);
}
+ public interface UpdateFunctor
+ {
+ void doUpdate();
+
+ void doCancel();
+ }
+
public static class Index implements Serializable
{
private static final long serialVersionUID = 1L;
@@ -119,18 +133,9 @@ public enum MapStorage
}
}
- public interface UpdateFunctor
- {
- void doUpdate();
-
- void doCancel();
- }
-
/**
* Checks whether all maps contain search indexes or updates them, if not.
- * @param msgId
- * @param context
- * @param fn
+ *
* @return True, if any maps where updated. False otherwise.
*/
public boolean updateMapsWithoutSearchIndex(@StringRes int msgId, Context context, final UpdateFunctor fn)
diff --git a/android/src/com/mapswithme/maps/MwmActivity.java b/android/src/com/mapswithme/maps/MwmActivity.java
index 80e5c5cbdf..0f671869a1 100644
--- a/android/src/com/mapswithme/maps/MwmActivity.java
+++ b/android/src/com/mapswithme/maps/MwmActivity.java
@@ -2,14 +2,11 @@ package com.mapswithme.maps;
import android.app.Activity;
import android.app.Dialog;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Location;
-import android.os.Build;
import android.os.Bundle;
-import android.os.Environment;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -59,7 +56,6 @@ import com.mapswithme.maps.widget.placepage.BasePlacePageAnimationController;
import com.mapswithme.maps.widget.placepage.PlacePageView;
import com.mapswithme.maps.widget.placepage.PlacePageView.State;
import com.mapswithme.util.BottomSheetHelper;
-import com.mapswithme.util.Constants;
import com.mapswithme.util.InputUtils;
import com.mapswithme.util.LocationUtils;
import com.mapswithme.util.UiUtils;
@@ -95,7 +91,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
private final static String EXTRA_LAT = "lat";
private final static String EXTRA_LON = "lon";
- private static final String[] DOCKED_FRAGMENTS = { SearchFragment.class.getName(), DownloadFragment.class.getName() };
+ private static final String[] DOCKED_FRAGMENTS = {SearchFragment.class.getName(), DownloadFragment.class.getName()};
// Need it for change map style
private static final String EXTRA_SET_MAP_STYLE = "set_map_style";
@@ -105,9 +101,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
// Map tasks that we run AFTER rendering initialized
private final Stack<MapTask> mTasks = new Stack<>();
- private BroadcastReceiver mExternalStorageReceiver;
private final StoragePathManager mPathManager = new StoragePathManager();
- private AlertDialog mStorageDisconnectedDialog;
private View mFrame;
@@ -123,12 +117,6 @@ public class MwmActivity extends BaseMwmFragmentActivity
private boolean mNeedCheckUpdate = true;
private int mLocationStateModeListenerId = LocationState.SLOT_UNDEFINED;
- // These flags are initialized to the invalid combination to force update on the first check
- // after launching.
- // These flags are static because the MwmActivity is recreated while screen orientation changing
- // but they shall not be reinitialized on screen orientation changing.
- private static boolean sStorageAvailable = false;
- private static boolean sStorageWritable = true;
private FadeView mFadeView;
@@ -259,29 +247,22 @@ public class MwmActivity extends BaseMwmFragmentActivity
private void checkLiteMapsInPro()
{
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT &&
- (Utils.isPackageInstalled(Constants.Package.MWM_LITE_PACKAGE) || Utils.isPackageInstalled(Constants.Package.MWM_SAMSUNG_PACKAGE)))
- {
- if (!mPathManager.containsLiteMapsOnSdcard())
- return;
-
- mPathManager.moveMapsLiteToPro(this,
- new MoveFilesListener()
- {
- @Override
- public void moveFilesFinished(String newPath)
- {
- UiUtils.showAlertDialog(MwmActivity.this, R.string.move_lite_maps_to_pro_ok);
- }
+ mPathManager.moveMapsLiteToPro(this,
+ new MoveFilesListener()
+ {
+ @Override
+ public void moveFilesFinished(String newPath)
+ {
+ UiUtils.showAlertDialog(MwmActivity.this, R.string.move_lite_maps_to_pro_ok);
+ }
- @Override
- public void moveFilesFailed(int errorCode)
- {
- UiUtils.showAlertDialog(MwmActivity.this, R.string.move_lite_maps_to_pro_failed);
- }
- }
- );
- }
+ @Override
+ public void moveFilesFailed(int errorCode)
+ {
+ UiUtils.showAlertDialog(MwmActivity.this, R.string.move_lite_maps_to_pro_failed);
+ }
+ }
+ );
}
private void checkUpdateMapsWithoutSearchIndex()
@@ -783,43 +764,36 @@ public class MwmActivity extends BaseMwmFragmentActivity
// Do not show this dialog on Kindle Fire - it doesn't have location services
// and even wifi settings can't be opened programmatically
- if (!Utils.isAmazonDevice())
- {
- new AlertDialog.Builder(this).setTitle(R.string.location_is_disabled_long_text)
- .setPositiveButton(R.string.connection_settings, new DialogInterface.OnClickListener()
+ if (Utils.isAmazonDevice())
+ return;
+
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.location_is_disabled_long_text)
+ .setPositiveButton(R.string.connection_settings, new DialogInterface.OnClickListener()
+ {
+ @Override
+ public void onClick(DialogInterface dialog, int which)
{
- @Override
- public void onClick(DialogInterface dialog, int which)
+ try
+ {
+ startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
+ } catch (final Exception e1)
{
+ // On older Android devices location settings are merged with security
try
{
- startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
- } catch (final Exception e1)
+ startActivity(new Intent(android.provider.Settings.ACTION_SECURITY_SETTINGS));
+ } catch (final Exception e2)
{
- // On older Android devices location settings are merged with security
- try
- {
- startActivity(new Intent(android.provider.Settings.ACTION_SECURITY_SETTINGS));
- } catch (final Exception e2)
- {
- Log.w(TAG, "Can't run activity" + e2);
- }
+ Log.w(TAG, "Can't run activity" + e2);
}
-
- dialog.dismiss();
- }
- })
- .setNegativeButton(R.string.close, new DialogInterface.OnClickListener()
- {
- @Override
- public void onClick(DialogInterface dialog, int which)
- {
- dialog.dismiss();
}
- })
- .create()
- .show();
- }
+
+ dialog.dismiss();
+ }
+ })
+ .setNegativeButton(R.string.close, null)
+ .show();
}
else if (errorCode == LocationHelper.ERROR_GPS_OFF)
{
@@ -920,11 +894,8 @@ public class MwmActivity extends BaseMwmFragmentActivity
listenLocationStateUpdates();
invalidateLocationState();
- startWatchingExternalStorage();
adjustZoomButtons(Framework.nativeIsRoutingActive());
-
mSearchController.refreshToolbar();
-
mPlacePage.onResume();
LikesManager.INSTANCE.showDialogs(this);
mMainMenu.onResume();
@@ -971,7 +942,6 @@ public class MwmActivity extends BaseMwmFragmentActivity
{
stopLocationStateUpdates();
pauseLocation();
- stopWatchingExternalStorage();
TtsPlayer.INSTANCE.stop();
LikesManager.INSTANCE.cancelDialogs();
super.onPause();
@@ -1004,89 +974,6 @@ public class MwmActivity extends BaseMwmFragmentActivity
LocationState.INSTANCE.invalidatePosition();
}
- private void updateExternalStorageState()
- {
- boolean available = false, writable = false;
- final String state = Environment.getExternalStorageState();
- if (Environment.MEDIA_MOUNTED.equals(state))
- available = writable = true;
- else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state))
- available = true;
-
- if (sStorageAvailable != available || sStorageWritable != writable)
- {
- sStorageAvailable = available;
- sStorageWritable = writable;
- handleExternalStorageState(available, writable);
- }
- }
-
- private void handleExternalStorageState(boolean available, boolean writeable)
- {
- if (available && writeable)
- {
- // Add local maps to the model
- mMapFragment.nativeStorageConnected();
-
- // @TODO enable downloader button and dismiss blocking popup
-
- if (mStorageDisconnectedDialog != null)
- mStorageDisconnectedDialog.dismiss();
- }
- else if (available)
- {
- // Add local maps to the model
- mMapFragment.nativeStorageConnected();
-
- // @TODO disable downloader button and dismiss blocking popup
-
- if (mStorageDisconnectedDialog != null)
- mStorageDisconnectedDialog.dismiss();
- }
- else
- {
- // Remove local maps from the model
- mMapFragment.nativeStorageDisconnected();
-
- // @TODO enable downloader button and show blocking popup
-
- if (mStorageDisconnectedDialog == null)
- {
- mStorageDisconnectedDialog = new AlertDialog.Builder(this)
- .setTitle(R.string.external_storage_is_not_available)
- .setMessage(getString(R.string.disconnect_usb_cable))
- .setCancelable(false)
- .create();
- }
- mStorageDisconnectedDialog.show();
- }
- }
-
- private void startWatchingExternalStorage()
- {
- mExternalStorageReceiver = new BroadcastReceiver()
- {
- @Override
- public void onReceive(Context context, Intent intent)
- {
- updateExternalStorageState();
- }
- };
-
- registerReceiver(mExternalStorageReceiver, StoragePathManager.getMediaChangesIntentFilter());
- updateExternalStorageState();
- }
-
- private void stopWatchingExternalStorage()
- {
- mPathManager.stopExternalStorageWatching();
- if (mExternalStorageReceiver != null)
- {
- unregisterReceiver(mExternalStorageReceiver);
- mExternalStorageReceiver = null;
- }
- }
-
@Override
public void onBackPressed()
{
@@ -1330,7 +1217,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
public boolean onTouch(View view, MotionEvent event)
{
return mPlacePage.hideOnTouch() ||
- mMapFragment.onTouch(view, event);
+ mMapFragment.onTouch(view, event);
}
@Override
diff --git a/android/src/com/mapswithme/maps/settings/StorageItem.java b/android/src/com/mapswithme/maps/settings/StorageItem.java
new file mode 100644
index 0000000000..208ad17710
--- /dev/null
+++ b/android/src/com/mapswithme/maps/settings/StorageItem.java
@@ -0,0 +1,52 @@
+package com.mapswithme.maps.settings;
+
+import com.mapswithme.util.Constants;
+
+/**
+ * Represents storage option.
+ */
+public class StorageItem
+{
+ // Path to the root of writable directory.
+ public final String mPath;
+ // Free size.
+ public final long mFreeSize;
+
+ StorageItem(String path, long size)
+ {
+ mPath = path;
+ mFreeSize = size;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (o == this)
+ return true;
+ if (o == null || !(o instanceof StorageItem))
+ return false;
+ StorageItem other = (StorageItem) o;
+ // Storage equal is considered equal, either its path OR size equals to another one's.
+ // Size of storage free space can change dynamically, so that hack provides us with better results identifying the same storages.
+ return mFreeSize == other.mFreeSize || mPath.equals(other.mPath);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ // Yes, do not put StorageItem to Hash containers, performance will be awful.
+ // At least such hash is compatible with hacky equals.
+ return 0;
+ }
+
+ @Override
+ public String toString()
+ {
+ return mPath + ", " + mFreeSize;
+ }
+
+ public String getFullPath()
+ {
+ return mPath + Constants.MWM_DIR_POSTFIX;
+ }
+}
diff --git a/android/src/com/mapswithme/maps/settings/StoragePathAdapter.java b/android/src/com/mapswithme/maps/settings/StoragePathAdapter.java
index 3c26df6c41..ff06071025 100644
--- a/android/src/com/mapswithme/maps/settings/StoragePathAdapter.java
+++ b/android/src/com/mapswithme/maps/settings/StoragePathAdapter.java
@@ -94,7 +94,7 @@ class StoragePathAdapter extends BaseAdapter
convertView = mInflater.inflate(R.layout.item_storage, parent, false);
CheckedTextView checkedView = (CheckedTextView) convertView;
- checkedView.setText(item.mPath + ": " + getSizeString(item.mSize));
+ checkedView.setText(item.mPath + ": " + getSizeString(item.mFreeSize));
checkedView.setChecked(storageIndex == mCurrentStorageIndex);
checkedView.setEnabled(storageIndex == mCurrentStorageIndex || isStorageBigEnough(storageIndex));
break;
@@ -114,7 +114,7 @@ class StoragePathAdapter extends BaseAdapter
{
final int index = getStorageIndex(position);
if (isStorageBigEnough(index) && index != mCurrentStorageIndex)
- mStoragePathManager.onStorageItemClick(index);
+ mStoragePathManager.changeStorage(index);
}
public void updateList(ArrayList<StorageItem> items, int currentItemIndex, long dirSize)
@@ -147,7 +147,7 @@ class StoragePathAdapter extends BaseAdapter
private boolean isStorageBigEnough(int index)
{
- return mItems.get(index).mSize >= mSizeNeeded;
+ return mItems.get(index).mFreeSize >= mSizeNeeded;
}
private int getStorageIndex(int position)
@@ -155,32 +155,4 @@ class StoragePathAdapter extends BaseAdapter
return position - HEADERS_COUNT;
}
- public static class StorageItem
- {
- public final String mPath;
- public final long mSize;
-
- StorageItem(String path, long size)
- {
- mPath = path;
- mSize = size;
- }
-
- @Override
- public boolean equals(Object o)
- {
- if (o == this)
- return true;
- if (o == null || !(o instanceof StorageItem))
- return false;
- StorageItem other = (StorageItem) o;
- return mSize == other.mSize && mPath.equals(other.mPath);
- }
-
- @Override
- public int hashCode()
- {
- return Long.valueOf(mSize).hashCode();
- }
- }
}
diff --git a/android/src/com/mapswithme/maps/settings/StoragePathFragment.java b/android/src/com/mapswithme/maps/settings/StoragePathFragment.java
index 74dc040ecb..350cc093e5 100644
--- a/android/src/com/mapswithme/maps/settings/StoragePathFragment.java
+++ b/android/src/com/mapswithme/maps/settings/StoragePathFragment.java
@@ -1,13 +1,17 @@
package com.mapswithme.maps.settings;
+import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
+import android.support.v7.app.AlertDialog;
import android.view.View;
import android.widget.ListView;
+import com.mapswithme.maps.R;
import com.mapswithme.maps.base.BaseMwmListFragment;
-import com.mapswithme.util.UiUtils;
+import com.mapswithme.util.Utils;
public class StoragePathFragment extends BaseMwmListFragment implements StoragePathManager.MoveFilesListener
{
@@ -35,12 +39,12 @@ public class StoragePathFragment extends BaseMwmListFragment implements StorageP
public void onReceive(Context context, Intent intent)
{
if (mAdapter != null)
- mAdapter.updateList(mPathManager.getStorageItems(), mPathManager.getCurrentStorageIndex(), StoragePathManager.getMwmDirSize());
+ mAdapter.updateList(mPathManager.getStorageItems(), mPathManager.getCurrentStorageIndex(), StorageUtils.getWritableDirSize());
}
};
mPathManager.startExternalStorageWatching(getActivity(), receiver, this);
initAdapter();
- mAdapter.updateList(mPathManager.getStorageItems(), mPathManager.getCurrentStorageIndex(), StoragePathManager.getMwmDirSize());
+ mAdapter.updateList(mPathManager.getStorageItems(), mPathManager.getCurrentStorageIndex(), StorageUtils.getWritableDirSize());
setListAdapter(mAdapter);
}
@@ -60,12 +64,27 @@ public class StoragePathFragment extends BaseMwmListFragment implements StorageP
@Override
public void moveFilesFinished(String newPath)
{
- mAdapter.updateList(mPathManager.getStorageItems(), mPathManager.getCurrentStorageIndex(), StoragePathManager.getMwmDirSize());
+ mAdapter.updateList(mPathManager.getStorageItems(), mPathManager.getCurrentStorageIndex(), StorageUtils.getWritableDirSize());
}
@Override
public void moveFilesFailed(int errorCode)
{
- UiUtils.showAlertDialog(getActivity(), "Failed to move maps with internal error :" + errorCode + ". Please contact us at bugs@maps.me and send this error code.");
+ if (!isAdded())
+ return;
+
+ final String message = "Failed to move maps with internal error :" + errorCode;
+ final Activity activity = getActivity();
+ new AlertDialog.Builder(activity)
+ .setTitle(message)
+ .setPositiveButton(R.string.report_a_bug, new DialogInterface.OnClickListener()
+ {
+ @Override
+ public void onClick(DialogInterface dialog, int which)
+ {
+ Utils.sendSupportMail(activity, message);
+ }
+ })
+ .show();
}
}
diff --git a/android/src/com/mapswithme/maps/settings/StoragePathManager.java b/android/src/com/mapswithme/maps/settings/StoragePathManager.java
index 95ab98c6e1..6b3a70adcb 100644
--- a/android/src/com/mapswithme/maps/settings/StoragePathManager.java
+++ b/android/src/com/mapswithme/maps/settings/StoragePathManager.java
@@ -1,32 +1,44 @@
package com.mapswithme.maps.settings;
-import android.annotation.TargetApi;
import android.app.Activity;
-import android.app.AlertDialog;
import android.app.ProgressDialog;
-import android.content.*;
-import android.os.AsyncTask;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.os.Build;
import android.os.Environment;
-import android.os.StatFs;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AlertDialog;
import android.util.Log;
-import com.mapswithme.maps.*;
+
+import com.mapswithme.maps.BuildConfig;
+import com.mapswithme.maps.Framework;
+import com.mapswithme.maps.MapStorage;
+import com.mapswithme.maps.MwmApplication;
+import com.mapswithme.maps.R;
import com.mapswithme.maps.bookmarks.data.BookmarkManager;
import com.mapswithme.util.Constants;
import com.mapswithme.util.UiUtils;
-import com.mapswithme.util.Utils;
import com.mapswithme.util.concurrency.ThreadPool;
import com.mapswithme.util.concurrency.UiThread;
-import java.io.*;
-import java.nio.channels.FileChannel;
-import java.util.*;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
-@SuppressWarnings("ResultOfMethodCallIgnored")
public class StoragePathManager
{
- private static final String[] MOVABLE_EXTS = Framework.nativeGetMovableFilesExts();
- private static final FilenameFilter MOVABLE_FILES_FILTER = new FilenameFilter()
+ static final String[] MOVABLE_EXTS = Framework.nativeGetMovableFilesExts();
+ static final FilenameFilter MOVABLE_FILES_FILTER = new FilenameFilter()
{
@Override
public boolean accept(File dir, String filename)
@@ -53,31 +65,25 @@ public class StoragePathManager
public static final int NOT_A_DIR_ERROR = 5;
public static final int UNKNOWN_KITKAT_ERROR = 6;
- private static String TAG = StoragePathManager.class.getName();
+ static final String TAG = StoragePathManager.class.getName();
private static final String LITE_SDCARD_PREFIX = "Android/data/com.mapswithme.maps/files";
private static final String SAMSUNG_LITE_SDCARD_PREFIX = "Android/data/com.mapswithme.maps.samsung/files";
private static final String PRO_SDCARD_PREFIX = "Android/data/com.mapswithme.maps.pro/files";
- private static final int VOLD_MODE = 1;
- private static final int MOUNTS_MODE = 2;
private static final String IS_KML_PLACED_IN_MAIN_STORAGE = "KmlBeenMoved";
private static final String IS_KITKAT_MIGRATION_COMPLETED = "KitKatMigrationCompleted";
private BroadcastReceiver mExternalReceiver;
private BroadcastReceiver mInternalReceiver;
private Activity mActivity;
- private ArrayList<StoragePathAdapter.StorageItem> mItems;
+ private ArrayList<StorageItem> mItems;
private int mCurrentStorageIndex = -1;
private MoveFilesListener mStorageListener;
/**
* Observes status of connected media and retrieves list of available external storages.
- *
- * @param activity context
- * @param receiver receiver to get broadcasts of media events. can be null
- * @param listener listener to get notifications about maps transfers. can be null
*/
- public void startExternalStorageWatching(Activity activity, BroadcastReceiver receiver, MoveFilesListener listener)
+ public void startExternalStorageWatching(Activity activity, @Nullable BroadcastReceiver receiver, @Nullable MoveFilesListener listener)
{
mActivity = activity;
mStorageListener = listener;
@@ -130,7 +136,7 @@ public class StoragePathManager
return mItems.size() > 1;
}
- public ArrayList<StoragePathAdapter.StorageItem> getStorageItems()
+ public ArrayList<StorageItem> getStorageItems()
{
return mItems;
}
@@ -142,115 +148,65 @@ public class StoragePathManager
public void updateExternalStorages()
{
- ArrayList<String> paths = new ArrayList<>();
+ List<String> pathsFromConfig = new ArrayList<>();
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT)
- parseKitkatStorages(paths);
+ StorageUtils.parseKitkatStorages(pathsFromConfig);
else
- parseStorages(paths);
+ StorageUtils.parseStorages(pathsFromConfig);
- Map<Long, String> pathsSizesMap = new HashMap<>();
+ mItems = new ArrayList<>();
- // Add current path first!
- final String currentStorageDir = getWritableDirRoot();
- addStoragePathWithSize(currentStorageDir, pathsSizesMap);
- for (String path : paths)
- addStoragePathWithSize(path, pathsSizesMap);
- addStoragePathWithSize(Environment.getExternalStorageDirectory().getAbsolutePath(), pathsSizesMap);
+ final StorageItem currentStorage = buildStorageItem(StorageUtils.getWritableDirRoot());
+ addStorageItem(currentStorage);
+ addStorageItem(buildStorageItem(Environment.getExternalStorageDirectory().getAbsolutePath()));
+ for (String path : pathsFromConfig)
+ addStorageItem(buildStorageItem(path));
- mItems = new ArrayList<>();
- mCurrentStorageIndex = -1;
- for (Map.Entry<Long, String> entry : pathsSizesMap.entrySet())
- {
- StoragePathAdapter.StorageItem item = new StoragePathAdapter.StorageItem(entry.getValue(), entry.getKey());
- mItems.add(item);
- if (item.mPath.equals(currentStorageDir))
- mCurrentStorageIndex = mItems.size() - 1;
- }
+ mCurrentStorageIndex = mItems.indexOf(currentStorage);
if (mCurrentStorageIndex == -1)
{
- Log.w(TAG, "Unrecognized current path: " + currentStorageDir);
- Log.w(TAG, "Parsed paths: " + Utils.mapPrettyPrint(pathsSizesMap));
+ Log.w(TAG, "Unrecognized current path : " + currentStorage);
+ Log.w(TAG, "Parsed paths : ");
+ for (StorageItem item : mItems)
+ Log.w(TAG, item.toString());
}
}
- @TargetApi(Build.VERSION_CODES.KITKAT)
- private static void parseKitkatStorages(List<String> paths)
+ private void addStorageItem(StorageItem item)
{
- File primaryStorage = MwmApplication.get().getExternalFilesDir(null);
- File[] storages = MwmApplication.get().getExternalFilesDirs(null);
- if (storages != null)
- {
- for (File f : storages)
- {
- // add only secondary dirs
- if (f != null && !f.equals(primaryStorage))
- {
- Log.i(TAG, "Additional storage path: " + f.getPath());
- paths.add(f.getPath());
- }
- }
- }
+ if (item != null && !mItems.contains(item))
+ mItems.add(item);
+ }
- ArrayList<String> testStorages = new ArrayList<>();
- parseStorages(testStorages);
- final String suffix = String.format(Constants.STORAGE_PATH, BuildConfig.APPLICATION_ID, Constants.FILES_DIR);
- for (String testStorage : testStorages)
+ private StorageItem buildStorageItem(String path)
+ {
+ try
{
- Log.i(TAG, "Test storage from config files : " + testStorage);
- if (isDirWritable(testStorage))
- {
- Log.i(TAG, "Found writable storage : " + testStorage);
- paths.add(testStorage);
- }
- else
+ final File f = new File(path + "/");
+ if (f.exists() && f.isDirectory() && f.canWrite() && StorageUtils.isDirWritable(path))
{
- testStorage += suffix;
- File file = new File(testStorage);
- if (!file.exists()) // create directory for our package if it isn't created by any reason
+ final long freeSize = StorageUtils.getFreeBytesAtPath(path);
+ if (freeSize > 0)
{
- Log.i(TAG, "Try to create MWM path");
- file.mkdirs();
- file = new File(testStorage);
- if (file.exists())
- Log.i(TAG, "Created!");
- }
- if (isDirWritable(testStorage))
- {
- Log.i(TAG, "Found writable storage : " + testStorage);
- paths.add(testStorage);
+ Log.i(TAG, "Storage found : " + path + ", size : " + freeSize);
+ return new StorageItem(path, freeSize);
}
}
- }
- }
-
- private static void parseStorages(ArrayList<String> paths)
- {
- parseMountFile("/etc/vold.conf", VOLD_MODE, paths);
- parseMountFile("/etc/vold.fstab", VOLD_MODE, paths);
- parseMountFile("/system/etc/vold.fstab", VOLD_MODE, paths);
- parseMountFile("/proc/mounts", MOUNTS_MODE, paths);
- }
-
- public static long getMwmDirSize()
- {
- final File writableDir = new File(Framework.nativeGetWritableDir());
- if (BuildConfig.DEBUG)
+ } catch (final IllegalArgumentException ex)
{
- if (!writableDir.exists())
- throw new IllegalStateException("Writable directory doesn't exits, can't get size.");
- if (!writableDir.isDirectory())
- throw new IllegalStateException("Writable directory isn't a directory, can't get size.");
+ Log.i(TAG, "Can't build storage for path : " + path);
}
- return getDirSizeRecursively(writableDir, MOVABLE_FILES_FILTER);
+ return null;
}
+ @SuppressWarnings("ResultOfMethodCallIgnored")
public boolean moveBookmarksToPrimaryStorage()
{
ArrayList<String> paths = new ArrayList<>();
- parseStorages(paths);
+ StorageUtils.parseStorages(paths);
List<String> approvedPaths = new ArrayList<>();
for (String path : paths)
@@ -279,7 +235,7 @@ public class StoragePathManager
for (File f : bookmarks)
bookmarksSize += f.length();
- if (getFreeBytesAtPath(bookmarkDir) < bookmarksSize)
+ if (StorageUtils.getFreeBytesAtPath(bookmarkDir) < bookmarksSize)
return false;
for (File oldBookmark : bookmarks)
@@ -287,7 +243,7 @@ public class StoragePathManager
String newBookmarkPath = BookmarkManager.generateUniqueBookmarkName(oldBookmark.getName().replace(bookmarkFileExt, ""));
try
{
- copyFile(oldBookmark, new File(newBookmarkPath));
+ StorageUtils.copyFile(oldBookmark, new File(newBookmarkPath));
oldBookmark.delete();
} catch (IOException e)
{
@@ -316,11 +272,11 @@ public class StoragePathManager
result.addAll(Arrays.asList(bookmarks));
}
- protected void onStorageItemClick(int index)
+ protected void changeStorage(int newIndex)
{
- final StoragePathAdapter.StorageItem oldItem = (mCurrentStorageIndex != -1) ? mItems.get(mCurrentStorageIndex) : null;
- final StoragePathAdapter.StorageItem item = mItems.get(index);
- final String path = getItemFullPath(item);
+ final StorageItem oldItem = (mCurrentStorageIndex != -1) ? mItems.get(mCurrentStorageIndex) : null;
+ final StorageItem item = mItems.get(newIndex);
+ final String path = item.getFullPath();
final File f = new File(path);
if (!f.exists() && !f.mkdirs())
@@ -329,8 +285,10 @@ public class StoragePathManager
return;
}
- new AlertDialog.Builder(mActivity).setCancelable(false).setTitle(R.string.move_maps)
- .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener()
+ new AlertDialog.Builder(mActivity)
+ .setCancelable(false)
+ .setTitle(R.string.move_maps)
+ .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
@@ -356,14 +314,15 @@ public class StoragePathManager
dlg.dismiss();
}
- }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener()
- {
- @Override
- public void onClick(DialogInterface dlg, int which)
- {
- dlg.dismiss();
- }
- }).create().show();
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener()
+ {
+ @Override
+ public void onClick(DialogInterface dlg, int which)
+ {
+ dlg.dismiss();
+ }
+ }).create().show();
}
/**
@@ -379,16 +338,16 @@ public class StoragePathManager
final String settingsDir = Framework.nativeGetSettingsDir();
final String writableDir = Framework.nativeGetWritableDir();
- if (settingsDir.equals(writableDir) || isDirWritable(writableDir))
+ if (settingsDir.equals(writableDir) || StorageUtils.isDirWritable(writableDir))
return;
- final long size = getMwmDirSize();
+ final long size = StorageUtils.getWritableDirSize();
updateExternalStorages();
- for (StoragePathAdapter.StorageItem item : mItems)
+ for (StorageItem item : mItems)
{
- if (item.mSize > size)
+ if (item.mFreeSize > size)
{
- setStoragePath(context, listener, item, new StoragePathAdapter.StorageItem(getWritableDirRoot(), 0),
+ setStoragePath(context, listener, item, new StorageItem(StorageUtils.getWritableDirRoot(), 0),
R.string.kitkat_optimization_in_progress);
return;
}
@@ -399,11 +358,11 @@ public class StoragePathManager
public void moveMapsLiteToPro(Context context, MoveFilesListener listener)
{
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT || !containsLiteMapsOnSdcard())
return;
- final long size = getMwmDirSize();
- StoragePathAdapter.StorageItem currentStorage = new StoragePathAdapter.StorageItem(getWritableDirRoot(), 0);
+ final long size = StorageUtils.getWritableDirSize();
+ final StorageItem currentStorage = new StorageItem(StorageUtils.getWritableDirRoot(), 0);
// there is no need to copy maps from primary external storage(on internal device flash memory) -
// maps are stored there in root folder and pro version can simply use them
@@ -411,13 +370,11 @@ public class StoragePathManager
return;
updateExternalStorages();
- for (StoragePathAdapter.StorageItem item : mItems)
+ for (StorageItem item : mItems)
{
- if (item.mSize > size && item.mPath.contains(PRO_SDCARD_PREFIX)
- && !item.mPath.equals(currentStorage.mPath))
+ if (item.mFreeSize > size && item.mPath.contains(PRO_SDCARD_PREFIX) && !item.mPath.equals(currentStorage.mPath))
{
- setStoragePath(context, listener, item, currentStorage,
- R.string.move_lite_maps_to_pro);
+ setStoragePath(context, listener, item, currentStorage, R.string.move_lite_maps_to_pro);
return;
}
}
@@ -425,9 +382,9 @@ public class StoragePathManager
listener.moveFilesFailed(UNKNOWN_LITE_PRO_ERROR);
}
- public boolean containsLiteMapsOnSdcard()
+ private boolean containsLiteMapsOnSdcard()
{
- final String storagePath = getWritableDirRoot();
+ final String storagePath = StorageUtils.getWritableDirRoot();
return storagePath.contains(LITE_SDCARD_PREFIX) || storagePath.contains(SAMSUNG_LITE_SDCARD_PREFIX);
}
@@ -437,7 +394,7 @@ public class StoragePathManager
* Bookmarks should be placed in main MapsWithMe directory on primary storage (eg. SettingsDir, where settings.ini file is placed). If they were copied
* to external storage (can happen on 2.6 and earlier mapswithme application versions) - we should move them back.
* <p/>
- * Data should be placed in private app directory on Kitkat+ devices, hence root of sdcard isn't writable anymore on them.
+ * Data should be placed in private app directory on Kitkat+ devices, hence root of sdcard isn't writable anymore there.
*/
public void checkKitkatMigration(final Activity activity)
{
@@ -494,95 +451,78 @@ public class StoragePathManager
private void migrateMaps(final Activity activity)
{
- if (!MwmApplication.get().nativeGetBoolean(IS_KITKAT_MIGRATION_COMPLETED, false))
- {
- checkExternalStoragePathOnKitkat(activity,
- new MoveFilesListener()
- {
- @Override
- public void moveFilesFinished(String newPath)
- {
- MwmApplication.get().nativeSetBoolean(IS_KITKAT_MIGRATION_COMPLETED, true);
- UiUtils.showAlertDialog(activity, R.string.kitkat_migrate_ok);
- }
-
- @Override
- public void moveFilesFailed(int errorCode)
- {
- UiUtils.showAlertDialog(activity, R.string.kitkat_migrate_failed);
- }
- }
- );
- }
- }
-
- private void setStoragePath(Context context, MoveFilesListener listener, StoragePathAdapter.StorageItem newStorage,
- StoragePathAdapter.StorageItem oldStorage, int messageId)
- {
- new MoveFilesTask(context, listener, newStorage, oldStorage, messageId).execute("");
- }
+ if (MwmApplication.get().nativeGetBoolean(IS_KITKAT_MIGRATION_COMPLETED, false))
+ return;
- /**
- * Recursively lists all movable files in the directory.
- */
- private static void listFilesRecursively(File dir, String prefix, FilenameFilter filter, ArrayList<String> relPaths)
- {
- for (File file : dir.listFiles())
+ checkExternalStoragePathOnKitkat(activity,
+ new MoveFilesListener()
+ {
+ @Override
+ public void moveFilesFinished(String newPath)
+ {
+ MwmApplication.get().nativeSetBoolean(IS_KITKAT_MIGRATION_COMPLETED, true);
+ UiUtils.showAlertDialog(activity, R.string.kitkat_migrate_ok);
+ }
+
+ @Override
+ public void moveFilesFailed(int errorCode)
+ {
+ UiUtils.showAlertDialog(activity, R.string.kitkat_migrate_failed);
+ }
+ }
+ );
+ }
+
+ private void setStoragePath(final Context context, final MoveFilesListener listener, final StorageItem newStorage,
+ final StorageItem oldStorage, final int messageId)
+ {
+ final ProgressDialog dialog = new ProgressDialog(context);
+ dialog.setMessage(context.getString(messageId));
+ dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+ dialog.setIndeterminate(true);
+ dialog.setCancelable(false);
+ dialog.show();
+
+ ThreadPool.getStorage().execute(new Runnable()
{
- if (file.isDirectory())
+ @Override
+ public void run()
{
- listFilesRecursively(file, prefix + file.getName() + File.separator, filter, relPaths);
- continue;
- }
- String name = file.getName();
- if (filter.accept(dir, name))
- relPaths.add(prefix + name);
- }
- }
+ final int result = changeStorage(newStorage, oldStorage);
- private static void removeEmptyDirectories(File dir)
- {
- for (File file : dir.listFiles())
- {
- if (!file.isDirectory())
- continue;
- removeEmptyDirectories(file);
- file.delete();
- }
- }
+ UiThread.run(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ if (dialog.isShowing())
+ dialog.dismiss();
- private static boolean removeFilesInDirectory(File dir, File[] files)
- {
- try
- {
- for (File file : files)
- {
- if (file != null)
- file.delete();
+ if (result == NO_ERROR)
+ listener.moveFilesFinished(newStorage.mPath);
+ else
+ listener.moveFilesFailed(result);
+
+ updateExternalStorages();
+ }
+ });
}
- removeEmptyDirectories(dir);
- return true;
- } catch (Exception e)
- {
- e.printStackTrace();
- return false;
- }
+ });
}
- private static int changeStorage(StoragePathAdapter.StorageItem newStorage, StoragePathAdapter.StorageItem oldStorage)
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ private int changeStorage(StorageItem newStorage, StorageItem oldStorage)
{
- final String fullNewPath = getItemFullPath(newStorage);
+ final String fullNewPath = newStorage.getFullPath();
- // According to onStorageItemClick code above, oldStorage can be null.
+ // According to changeStorage code above, oldStorage can be null.
if (oldStorage == null)
{
Log.w(TAG, "Old storage path is null. New path is: " + fullNewPath);
return NULL_ERROR;
}
- final String fullOldPath = getItemFullPath(oldStorage);
-
- final File oldDir = new File(fullOldPath);
+ final File oldDir = new File(oldStorage.getFullPath());
final File newDir = new File(fullNewPath);
if (!newDir.exists())
newDir.mkdir();
@@ -593,12 +533,12 @@ public class StoragePathManager
throw new IllegalStateException("Cannot move maps. New path is not a directory. New path : " + newDir);
if (!oldDir.isDirectory())
throw new IllegalStateException("Cannot move maps. Old path is not a directory. Old path : " + oldDir);
- if (!isDirWritable(fullNewPath))
+ if (!StorageUtils.isDirWritable(fullNewPath))
throw new IllegalStateException("Cannot move maps. New path is not writable. New path : " + fullNewPath);
}
ArrayList<String> relPaths = new ArrayList<>();
- listFilesRecursively(oldDir, "", MOVABLE_FILES_FILTER, relPaths);
+ StorageUtils.listFilesRecursively(oldDir, "", MOVABLE_FILES_FILTER, relPaths);
File[] oldFiles = new File[relPaths.size()];
File[] newFiles = new File[relPaths.size()];
@@ -617,7 +557,7 @@ public class StoragePathManager
File parent = newFiles[i].getParentFile();
if (parent != null)
parent.mkdirs();
- copyFile(oldFiles[i], newFiles[i]);
+ StorageUtils.copyFile(oldFiles[i], newFiles[i]);
}
else
{
@@ -630,7 +570,7 @@ public class StoragePathManager
e.printStackTrace();
// In the case of failure delete all new files. Old files will
// be lost if new files were just moved from old locations.
- removeFilesInDirectory(newDir, newFiles);
+ StorageUtils.removeFilesInDirectory(newDir, newFiles);
return IOEXCEPTION_ERROR;
}
@@ -644,231 +584,7 @@ public class StoragePathManager
});
// Delete old files because new files were successfully created.
- removeFilesInDirectory(oldDir, oldFiles);
+ StorageUtils.removeFilesInDirectory(oldDir, oldFiles);
return NO_ERROR;
}
-
- private static void copyFile(File source, File dest) throws IOException
- {
- int maxChunkSize = 10 * Constants.MB; // move file by smaller chunks to avoid OOM.
- FileChannel inputChannel = null, outputChannel = null;
- try
- {
- inputChannel = new FileInputStream(source).getChannel();
- outputChannel = new FileOutputStream(dest).getChannel();
- long totalSize = inputChannel.size();
-
- for (long currentPosition = 0; currentPosition < totalSize; currentPosition += maxChunkSize)
- {
- outputChannel.position(currentPosition);
- outputChannel.transferFrom(inputChannel, currentPosition, maxChunkSize);
- }
- } finally
- {
- if (inputChannel != null)
- inputChannel.close();
- if (outputChannel != null)
- outputChannel.close();
- }
- }
-
- private static long getDirSizeRecursively(File file, FilenameFilter fileFilter)
- {
- if (file.isDirectory())
- {
- long dirSize = 0;
- for (File child : file.listFiles())
- dirSize += getDirSizeRecursively(child, fileFilter);
-
- return dirSize;
- }
-
- if (fileFilter.accept(file.getParentFile(), file.getName()))
- return file.length();
-
- return 0;
- }
-
-
- private class MoveFilesTask extends AsyncTask<String, Void, Integer>
- {
- private final ProgressDialog mDlg;
- private final StoragePathAdapter.StorageItem mNewStorage;
- private final StoragePathAdapter.StorageItem mOldStorage;
- private final MoveFilesListener mListener;
-
- public MoveFilesTask(Context context, MoveFilesListener listener, StoragePathAdapter.StorageItem newStorage,
- StoragePathAdapter.StorageItem oldStorage, int messageID)
- {
- mNewStorage = newStorage;
- mOldStorage = oldStorage;
- mListener = listener;
-
- mDlg = new ProgressDialog(context);
- mDlg.setMessage(context.getString(messageID));
- mDlg.setProgressStyle(ProgressDialog.STYLE_SPINNER);
- mDlg.setIndeterminate(true);
- mDlg.setCancelable(false);
- }
-
- @Override
- protected void onPreExecute()
- {
- mDlg.show();
- }
-
- @Override
- protected Integer doInBackground(String... params)
- {
- return changeStorage(mNewStorage, mOldStorage);
- }
-
- @Override
- protected void onPostExecute(Integer result)
- {
- // Using dummy try-catch because of the following:
- // http://stackoverflow.com/questions/2745061/java-lang-illegalargumentexception-view-not-attached-to-window-manager
- try
- {
- mDlg.dismiss();
- } catch (final Exception e)
- {
- e.printStackTrace();
- }
-
- if (result == NO_ERROR)
- mListener.moveFilesFinished(mNewStorage.mPath);
- else
- mListener.moveFilesFailed(result);
-
- updateExternalStorages();
- }
- }
-
- private static void addStoragePathWithSize(String path, Map<Long, String> sizesPaths)
- {
- Log.i(TAG, "Trying to add path " + path);
- try
- {
- final File f = new File(path + "/");
- if (f.exists() && f.isDirectory() && f.canWrite() && isDirWritable(path))
- {
- final long size = getFreeBytesAtPath(path);
-
- if (size > 0 && !sizesPaths.containsKey(size))
- {
- Log.i(TAG, "Path added: " + path + ", size = " + size);
- sizesPaths.put(size, path);
- }
- }
- } catch (final IllegalArgumentException ex)
- {
- // Suppress exceptions for unavailable storages.
- Log.i(TAG, "StatFs error for path: " + path);
- }
- }
-
- // 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
- private static void parseMountFile(String file, int mode, ArrayList<String> paths)
- {
- Log.i(TAG, "Parsing " + file);
-
- BufferedReader reader = null;
- try
- {
- reader = new BufferedReader(new FileReader(file));
-
- while (true)
- {
- final String line = reader.readLine();
- if (line == null)
- break;
-
- // standard regexp for all possible whitespaces (space, tab, etc)
- final String[] arr = line.split("\\s+");
-
- // split may return empty first strings
- int start = 0;
- while (start < arr.length && arr[start].length() == 0)
- ++start;
-
- if (arr.length - start > 3)
- {
- if (arr[start].charAt(0) == '#')
- continue;
-
- if (mode == VOLD_MODE)
- {
- if (arr[start].startsWith("dev_mount"))
- paths.add(arr[start + 2]);
- }
- else
- {
- final String prefixes[] = {"tmpfs", "/dev/block/vold", "/dev/fuse", "/mnt/media_rw"};
- for (final String s : prefixes)
- if (arr[start].startsWith(s))
- paths.add(arr[start + 1]);
- }
- }
- }
- } catch (final IOException e)
- {
- Log.w(TAG, "Can't read file: " + file);
- } finally
- {
- Utils.closeStream(reader);
- }
- }
-
- private static long getFreeBytesAtPath(String path)
- {
- long size = 0;
- try
- {
- size = new File(path).getFreeSpace();
- } catch (RuntimeException e)
- {
- e.printStackTrace();
- }
-
- return size;
- }
-
- /**
- * Check if directory is writable. On some devices with KitKat (eg, Samsung S4) simple File.canWrite() returns
- * true for some actually read only directories on sdcard.
- * see https://code.google.com/p/android/issues/detail?id=66369 for details
- *
- * @param path path to ckeck
- * @return result
- */
- private static boolean isDirWritable(String path)
- {
- final File f = new File(path + "/testDir");
- f.mkdir();
- if (f.exists())
- {
- f.delete();
- return true;
- }
-
- return false;
- }
-
- private static String getItemFullPath(StoragePathAdapter.StorageItem item)
- {
- return item.mPath + Constants.MWM_DIR_POSTFIX;
- }
-
- private static String getWritableDirRoot()
- {
- String writableDir = Framework.nativeGetWritableDir();
- int index = writableDir.lastIndexOf(Constants.MWM_DIR_POSTFIX);
- if (index != -1)
- writableDir = writableDir.substring(0, index);
-
- return writableDir;
- }
}
diff --git a/android/src/com/mapswithme/maps/settings/StorageUtils.java b/android/src/com/mapswithme/maps/settings/StorageUtils.java
new file mode 100644
index 0000000000..71f3fcda3a
--- /dev/null
+++ b/android/src/com/mapswithme/maps/settings/StorageUtils.java
@@ -0,0 +1,297 @@
+package com.mapswithme.maps.settings;
+
+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;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.List;
+
+public final class StorageUtils
+{
+ private StorageUtils() {}
+
+ private static final int VOLD_MODE = 1;
+ private static final int MOUNTS_MODE = 2;
+
+ /**
+ * Check if directory is writable. On some devices with KitKat (eg, Samsung S4) simple File.canWrite() returns
+ * true for some actually read only directories on sdcard.
+ * see https://code.google.com/p/android/issues/detail?id=66369 for details
+ *
+ * @param path path to ckeck
+ * @return result
+ */
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ static boolean isDirWritable(String path)
+ {
+ final File f = new File(path, "testDir");
+ f.mkdir();
+ if (f.exists())
+ {
+ f.delete();
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns path, where maps and other files are stored.
+ * @return pat (or empty string, if framework wasn't created yet)
+ */
+ static String getWritableDirRoot()
+ {
+ String writableDir = Framework.nativeGetWritableDir();
+ int index = writableDir.lastIndexOf(Constants.MWM_DIR_POSTFIX);
+ if (index != -1)
+ writableDir = writableDir.substring(0, index);
+
+ return writableDir;
+ }
+
+ static long getFreeBytesAtPath(String path)
+ {
+ long size = 0;
+ try
+ {
+ size = new File(path).getFreeSpace();
+ } catch (RuntimeException e)
+ {
+ e.printStackTrace();
+ }
+
+ return size;
+ }
+
+ // 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)
+ {
+ Log.i(StoragePathManager.TAG, "Parsing " + file);
+
+ BufferedReader reader = null;
+ try
+ {
+ reader = new BufferedReader(new FileReader(file));
+
+ while (true)
+ {
+ final String line = reader.readLine();
+ if (line == null)
+ break;
+
+ // standard regexp for all possible whitespaces (space, tab, etc)
+ final String[] arr = line.split("\\s+");
+
+ // split may return empty first strings
+ int start = 0;
+ while (start < arr.length && arr[start].length() == 0)
+ ++start;
+
+ if (arr.length - start <= 3)
+ continue;
+
+ if (arr[start].charAt(0) == '#')
+ continue;
+
+ if (mode == VOLD_MODE)
+ {
+ if (arr[start].startsWith("dev_mount"))
+ paths.add(arr[start + 2]);
+ }
+ else
+ {
+ for (final String s : new String[]{"tmpfs", "/dev/block/vold", "/dev/fuse", "/mnt/media_rw"})
+ if (arr[start].startsWith(s))
+ paths.add(arr[start + 1]);
+ }
+ }
+ } catch (final IOException e)
+ {
+ Log.w(StoragePathManager.TAG, "Can't read file: " + file);
+ } finally
+ {
+ Utils.closeStream(reader);
+ }
+ }
+
+ static void parseStorages(List<String> paths)
+ {
+ parseMountFile("/etc/vold.conf", VOLD_MODE, paths);
+ parseMountFile("/etc/vold.fstab", VOLD_MODE, paths);
+ parseMountFile("/system/etc/vold.fstab", VOLD_MODE, paths);
+ parseMountFile("/proc/mounts", MOUNTS_MODE, paths);
+ }
+
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ static void parseKitkatStorages(List<String> paths)
+ {
+ final File primaryStorage = MwmApplication.get().getExternalFilesDir(null);
+ final File[] storages = MwmApplication.get().getExternalFilesDirs(null);
+ if (storages != null)
+ {
+ for (File f : storages)
+ {
+ // add only secondary dirs
+ if (f != null && !f.equals(primaryStorage))
+ {
+ Log.i(StoragePathManager.TAG, "Additional storage path: " + f.getPath());
+ paths.add(f.getPath());
+ }
+ }
+ }
+
+ final ArrayList<String> testStorages = new ArrayList<>();
+ parseStorages(testStorages);
+ final String suffix = String.format(Constants.STORAGE_PATH, BuildConfig.APPLICATION_ID, Constants.FILES_DIR);
+ for (String testStorage : testStorages)
+ {
+ Log.i(StoragePathManager.TAG, "Test storage from config files : " + testStorage);
+ if (isDirWritable(testStorage))
+ {
+ Log.i(StoragePathManager.TAG, "Found writable storage : " + testStorage);
+ paths.add(testStorage);
+ }
+ else
+ {
+ testStorage += suffix;
+ File file = new File(testStorage);
+ if (!file.exists()) // create directory for our package if it isn't created by any reason
+ {
+ Log.i(StoragePathManager.TAG, "Try to create MWM path");
+ file.mkdirs();
+ file = new File(testStorage);
+ if (file.exists())
+ Log.i(StoragePathManager.TAG, "Created!");
+ }
+ if (isDirWritable(testStorage))
+ {
+ Log.i(StoragePathManager.TAG, "Found writable storage : " + testStorage);
+ paths.add(testStorage);
+ }
+ }
+ }
+ }
+
+ static void copyFile(File source, File dest) throws IOException
+ {
+ int maxChunkSize = 10 * Constants.MB; // move file by smaller chunks to avoid OOM.
+ FileChannel inputChannel = null, outputChannel = null;
+ try
+ {
+ inputChannel = new FileInputStream(source).getChannel();
+ outputChannel = new FileOutputStream(dest).getChannel();
+ long totalSize = inputChannel.size();
+
+ for (long currentPosition = 0; currentPosition < totalSize; currentPosition += maxChunkSize)
+ {
+ outputChannel.position(currentPosition);
+ outputChannel.transferFrom(inputChannel, currentPosition, maxChunkSize);
+ }
+ } finally
+ {
+ if (inputChannel != null)
+ inputChannel.close();
+ if (outputChannel != null)
+ outputChannel.close();
+ }
+ }
+
+ static long getDirSizeRecursively(File file, FilenameFilter fileFilter)
+ {
+ if (file.isDirectory())
+ {
+ long dirSize = 0;
+ for (File child : file.listFiles())
+ dirSize += getDirSizeRecursively(child, fileFilter);
+
+ return dirSize;
+ }
+
+ if (fileFilter.accept(file.getParentFile(), file.getName()))
+ return file.length();
+
+ return 0;
+ }
+
+ static long getWritableDirSize()
+ {
+ final File writableDir = new File(Framework.nativeGetWritableDir());
+ if (BuildConfig.DEBUG)
+ {
+ if (!writableDir.exists())
+ throw new IllegalStateException("Writable directory doesn't exits, can't get size.");
+ if (!writableDir.isDirectory())
+ throw new IllegalStateException("Writable directory isn't a directory, can't get size.");
+ }
+
+ return getDirSizeRecursively(writableDir, StoragePathManager.MOVABLE_FILES_FILTER);
+ }
+
+ /**
+ * Recursively lists all movable files in the directory.
+ */
+ static void listFilesRecursively(File dir, String prefix, FilenameFilter filter, ArrayList<String> relPaths)
+ {
+ for (File file : dir.listFiles())
+ {
+ if (file.isDirectory())
+ {
+ listFilesRecursively(file, prefix + file.getName() + File.separator, filter, relPaths);
+ continue;
+ }
+ String name = file.getName();
+ if (filter.accept(dir, name))
+ relPaths.add(prefix + name);
+ }
+ }
+
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ static void removeEmptyDirectories(File dir)
+ {
+ for (File file : dir.listFiles())
+ {
+ if (!file.isDirectory())
+ continue;
+ removeEmptyDirectories(file);
+ file.delete();
+ }
+ }
+
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ static boolean removeFilesInDirectory(File dir, File[] files)
+ {
+ try
+ {
+ for (File file : files)
+ {
+ if (file != null)
+ file.delete();
+ }
+ removeEmptyDirectories(dir);
+ return true;
+ } catch (Exception e)
+ {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+}