[android] map storage refactoring

This commit is contained in:
ExMix 2014-05-07 10:55:22 +03:00 committed by Alex Zolotarev
parent 9d69f166d8
commit 0b64872239
10 changed files with 819 additions and 662 deletions

View file

@ -4,6 +4,7 @@
#include "../core/jni_helper.hpp"
#include "../core/render_context.hpp"
#include "../platform/Platform.hpp"
#include "../../../../../map/framework.hpp"
#include "../../../../../map/measurement_utils.hpp"
@ -39,10 +40,7 @@ android::Framework * g_framework = 0;
namespace android
{
void Framework::CallRepaint()
{
//LOG(LINFO, ("Calling Repaint"));
}
void Framework::CallRepaint() {}
Framework::Framework()
: m_mask(0),
@ -54,12 +52,6 @@ namespace android
g_framework = this;
m_videoTimer = new VideoTimer(bind(&Framework::CallRepaint, this));
/*
size_t const measurementsCount = 5;
m_sensors[0].SetCount(measurementsCount);
m_sensors[1].SetCount(measurementsCount);
*/
}
Framework::~Framework()
@ -89,9 +81,7 @@ namespace android
void Framework::UpdateCompassSensor(int ind, float * arr)
{
//LOG ( LINFO, ("Sensors before, C++: ", arr[0], arr[1], arr[2]) );
m_sensors[ind].Next(arr);
//LOG ( LINFO, ("Sensors after, C++: ", arr[0], arr[1], arr[2]) );
}
void Framework::DeleteRenderPolicy()
@ -501,7 +491,7 @@ namespace android
{
ASSERT ( out.empty(), () );
Platform const & pl = GetPlatform();
::Platform const & pl = GetPlatform();
vector<string> v;
m_work.GetLocalMaps(v);
@ -949,4 +939,57 @@ extern "C"
g_framework->Invalidate();
}
JNIEXPORT jstring JNICALL
Java_com_mapswithme_maps_Framework_nativeGetBookmarkDir(JNIEnv * env, jclass thiz)
{
return jni::ToJavaString(env, GetPlatform().SettingsDir().c_str());
}
JNIEXPORT jstring JNICALL
Java_com_mapswithme_maps_Framework_nativeGetWritableDir(JNIEnv * env, jclass thiz)
{
return jni::ToJavaString(env, GetPlatform().WritableDir().c_str());
}
JNIEXPORT jstring JNICALL
Java_com_mapswithme_maps_Framework_nativeGetSettingsDir(JNIEnv * env, jclass thiz)
{
return jni::ToJavaString(env, GetPlatform().SettingsDir().c_str());
}
JNIEXPORT jobjectArray JNICALL
Java_com_mapswithme_maps_Framework_nativeGetMovablefilesExt(JNIEnv * env, jclass thiz)
{
jstring fakeString = jni::ToJavaString(env, "");
jclass stringClass = env->GetObjectClass(fakeString);
string exts[] = { DATA_FILE_EXTENSION, FONT_FILE_EXTENSION};
jobjectArray resultArray = env->NewObjectArray(ARRAY_SIZE(exts), stringClass, NULL);
for (int i = 0; i < ARRAY_SIZE(exts); ++i)
env->SetObjectArrayElement(resultArray, i, jni::ToJavaString(env, exts[i]));
return resultArray;
}
JNIEXPORT jstring JNICALL
Java_com_mapswithme_maps_Framework_nativeGetBookmarksExt(JNIEnv * env, jclass thiz)
{
return jni::ToJavaString(env, BOOKMARKS_FILE_EXTENSION);
}
JNIEXPORT void JNICALL
Java_com_mapswithme_maps_Framework_nativeSetWritableDir(JNIEnv * env, jclass thiz, jstring _newPath)
{
string newPath = jni::ToNativeString(env, _newPath);
g_framework->RemoveLocalMaps();
android::Platform::Instance().SetStoragePath(newPath);
g_framework->AddLocalMaps();
}
JNIEXPORT void JNICALL
Java_com_mapswithme_maps_Framework_nativeLoadbookmarks(JNIEnv * env, jclass thiz)
{
g_framework->NativeFramework()->LoadBookmarks();
}
}

View file

@ -1,158 +1,19 @@
#include "../platform/Platform.hpp"
#include "../core/jni_helper.hpp"
#include "Framework.hpp"
#include "../platform/Platform.hpp"
#include "../../../../../map/bookmark.hpp"
#include "../../../../../base/stl_add.hpp"
#include "../../../../../coding/file_name_utils.hpp"
#include "../../../../../coding/internal/file_data.hpp"
#include "../../../../../std/set.hpp"
#include "../../../../../std/string.hpp"
#include "../../../../../std/algorithm.hpp"
namespace
{
struct PathInserter
{
public:
PathInserter(string const & dirPath, set<string> & set)
: m_dirPath(dirPath)
, m_set(set)
{
if (m_dirPath[m_dirPath.size() - 1] != '/')
m_dirPath = m_dirPath + "/";
}
void operator() (string const & name)
{
m_set.insert(m_dirPath + name);
}
private:
string m_dirPath;
set<string> & m_set;
};
}
extern "C"
{
JNIEXPORT jstring JNICALL
Java_com_mapswithme_util_StoragePathManager_nativeGetBookmarkDir(JNIEnv * env, jclass thiz)
Java_com_mapswithme_util_StoragePathManager_nativeGenerateUniqueBookmarkName(JNIEnv * env, jclass thiz, jstring _baseName)
{
return jni::ToJavaString(env, GetPlatform().SettingsDir().c_str());
}
JNIEXPORT jstring JNICALL
Java_com_mapswithme_util_StoragePathManager_nativeGetWritableDir(JNIEnv * env, jclass thiz)
{
return jni::ToJavaString(env, GetPlatform().WritableDir().c_str());
}
JNIEXPORT jstring JNICALL
Java_com_mapswithme_util_StoragePathManager_nativeGetSettingsDir(JNIEnv * env, jclass thiz)
{
return jni::ToJavaString(env, GetPlatform().SettingsDir().c_str());
}
JNIEXPORT jboolean JNICALL
Java_com_mapswithme_util_StoragePathManager_nativeSetStoragePath(JNIEnv * env, jobject thiz,
jstring s)
{
string const from = GetPlatform().WritableDir();
string const to = jni::ToNativeString(env, s);
// Remove all maps from container.
g_framework->RemoveLocalMaps();
// Get files to copy.
Platform & pl = GetPlatform();
// Get regexp like this: (\.mwm$|\.ttf$)
string const regexp = "(" "\\"DATA_FILE_EXTENSION"$" "|"
"\\"FONT_FILE_EXTENSION"$" ")";
Platform::FilesList files;
pl.GetFilesByRegExp(from, regexp, files);
// Copy all needed files.
for (size_t i = 0; i < files.size(); ++i)
if (!my::CopyFileX(from + files[i], to + files[i]))
{
// Do the undo - delete all previously copied files.
for (size_t j = 0; j <= i; ++j)
{
string const path = to + files[j];
VERIFY ( my::DeleteFileX(path), (path) );
}
return false;
}
// Set new storage path.
android::Platform::Instance().SetStoragePath(to);
// Add all maps again.
g_framework->AddLocalMaps();
// Reload bookmarks again
g_framework->NativeFramework()->LoadBookmarks();
return true;
}
JNIEXPORT jboolean JNICALL
Java_com_mapswithme_util_StoragePathManager_nativeMoveBookmarks(JNIEnv * env, jclass thiz, jobjectArray pathArray, jlong storageAvSize)
{
set<string> fullBookmarkSet;
Platform & pl = GetPlatform();
string const settingsDir = pl.SettingsDir();
string const writableDir = pl.WritableDir();
if (writableDir != settingsDir)
{
Platform::FilesList list;
pl.GetFilesByExt(writableDir, BOOKMARKS_FILE_EXTENSION, list);
for_each(list.begin(), list.end(), PathInserter(writableDir, fullBookmarkSet));
}
int const arraySize = env->GetArrayLength(pathArray);
for (int i = 0; i < arraySize; ++i)
{
jstring jPath = (jstring)env->GetObjectArrayElement(pathArray, i);
string path = jni::ToNativeString(env, jPath);
if (path != settingsDir)
{
Platform::FilesList list;
pl.GetFilesByExt(path, BOOKMARKS_FILE_EXTENSION, list);
for_each(list.begin(), list.end(), PathInserter(path, fullBookmarkSet));
}
}
typedef set<string>::const_iterator fileIt;
uint64_t fullSize = 0;
for (fileIt it = fullBookmarkSet.begin(); it != fullBookmarkSet.end(); ++it)
{
uint64_t size = 0;
my::GetFileSize(*it, size);
fullSize += size;
}
if (storageAvSize < fullSize)
return false;
for (fileIt it = fullBookmarkSet.begin(); it != fullBookmarkSet.end(); ++it)
{
string const oldFilePath = *it;
string fileName = oldFilePath;
my::GetNameFromFullPath(fileName);
my::GetNameWithoutExt(fileName);
string const newFilePath = BookmarkCategory::GenerateUniqueFileName(settingsDir, fileName);
if (my::CopyFileX(oldFilePath, newFilePath))
my::DeleteFileX(oldFilePath);
}
g_framework->NativeFramework()->LoadBookmarks();
return true;
string baseName = jni::ToNativeString(env, _baseName);
string bookmarkFileName = BookmarkCategory::GenerateUniqueFileName(GetPlatform().SettingsDir(), baseName);
return jni::ToJavaString(env, bookmarkFileName);
}
}

View file

@ -1,20 +1,9 @@
#include "../Framework.hpp"
#include "../../core/jni_helper.hpp"
#include "../../platform/Platform.hpp"
#include "../../../../../../coding/internal/file_data.hpp"
extern "C"
{
JNIEXPORT jstring JNICALL
Java_com_mapswithme_maps_settings_StoragePathActivity_nativeGetStoragePath(JNIEnv * env, jobject thiz)
{
return jni::ToJavaString(env, android::Platform::Instance().GetStoragePathPrefix());
}
JNIEXPORT jboolean JNICALL
Java_com_mapswithme_maps_settings_SettingsActivity_isDownloadingActive(JNIEnv * env, jobject thiz)
{

View file

@ -4,31 +4,37 @@
<Preference
android:key="@string/pref_storage_activity"
android:summary="@string/maps_storage_summary"
android:title="@string/maps_storage" />
android:title="@string/maps_storage"
android:order="0"/>
<ListPreference
android:entries="@array/measument_units"
android:entryValues="@array/measument_units_values"
android:key="@string/pref_munits"
android:summary="@string/measurement_units_summary"
android:title="@string/measurement_units" />
android:title="@string/measurement_units"
android:order="1"/>
<CheckBoxPreference
android:key="@string/pref_allow_stat"
android:summary="@string/allow_statistics_hint"
android:title="@string/allow_statistics" />
android:title="@string/allow_statistics"
android:order="2"/>
<CheckBoxPreference
android:key="@string/pref_zoom_btns_enabled"
android:summary="@string/pref_zoom_summary"
android:title="@string/pref_zoom_title" />
android:title="@string/pref_zoom_title"
android:order="3"/>
<Preference
android:key="@string/pref_yota"
android:summary="@string/yopme_pref_summary"
android:title="@string/yopme_pref_title" />
android:title="@string/yopme_pref_title"
android:order="98"/>
<Preference
android:key="@string/pref_about"
android:title="@string/about_menu_title" />
android:title="@string/about_menu_title"
android:order="99"/>
</PreferenceScreen>

View file

@ -22,6 +22,41 @@ public class Framework
public void onAdditionalLayerActivated(long index);
public void onDismiss();
}
static public String GetSettingsDir()
{
return nativeGetSettingsDir();
}
static public String GetBookmarksDir()
{
return nativeGetBookmarkDir();
}
static public String GetWritableDir()
{
return nativeGetWritableDir();
}
static public String GetBookmarkFileExt()
{
return nativeGetBookmarksExt();
}
static public String[] GetMovableFilesExt()
{
return nativeGetMovablefilesExt();
}
static public void SetWritableDir(String newPath)
{
nativeSetWritableDir(newPath);
}
static public void ReloadBookmarks()
{
nativeLoadbookmarks();
}
// Interface
@ -135,4 +170,12 @@ public class Framework
public native static void cleanSearchLayerOnMap();
public native static void invalidate();
public native static String getPoiTypeNameForLatLon(double lat, double lon);
private native static String[] nativeGetMovablefilesExt();
private native static String nativeGetBookmarksExt();
private native static String nativeGetBookmarkDir();
private native static String nativeGetSettingsDir();
private native static String nativeGetWritableDir();
private native static void nativeSetWritableDir(String newPath);
private native static void nativeLoadbookmarks();
}

View file

@ -12,7 +12,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnKeyListener;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.location.Location;
import android.net.Uri;
@ -118,6 +117,11 @@ public class MWMActivity extends NvEventQueueActivity
{
return mApplication.getLocationState();
}
private StoragePathManager GetPathManager()
{
return mApplication.GetPathManager();
}
private void startLocation()
{
@ -337,11 +341,12 @@ public class MWMActivity extends NvEventQueueActivity
final String KitKatMigrationCompleted = "KitKatMigrationCompleted";
final boolean kmlMoved = MWMApplication.get().nativeGetBoolean(KmlMovedFlag, false);
final boolean mapsCpy = MWMApplication.get().nativeGetBoolean(KitKatMigrationCompleted, false);
StoragePathManager pathManager = mApplication.GetPathManager();
if (!kmlMoved)
{
if (StoragePathManager.MoveBookmarks())
MWMApplication.get().nativeSetBoolean(KmlMovedFlag, true);
if (pathManager.MoveBookmarks())
mApplication.nativeSetBoolean(KmlMovedFlag, true);
else
{
ShowAlertDlg(R.string.bookmark_move_fail);
@ -356,12 +361,17 @@ public class MWMActivity extends NvEventQueueActivity
@Override
public void MoveFilesFinished(String newPath)
{
MWMApplication.get().nativeSetBoolean(KitKatMigrationCompleted, true);
mApplication.nativeSetBoolean(KitKatMigrationCompleted, true);
ShowAlertDlg(R.string.kitkat_migrate_ok);
}
@Override
public void MoveFilesFailed()
{
ShowAlertDlg(R.string.kitkat_migrate_failed);
}
};
if (StoragePathManager.CheckWritableDir(this, listener) == false)
ShowAlertDlg(R.string.kitkat_migrate_failed);
pathManager.CheckWritableDir(this, listener);
}
}
@ -1129,19 +1139,7 @@ public class MWMActivity extends NvEventQueueActivity
}
};
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
filter.addAction(Intent.ACTION_MEDIA_REMOVED);
filter.addAction(Intent.ACTION_MEDIA_EJECT);
filter.addAction(Intent.ACTION_MEDIA_SHARED);
filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
filter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL);
filter.addAction(Intent.ACTION_MEDIA_UNMOUNTABLE);
filter.addAction(Intent.ACTION_MEDIA_CHECKING);
filter.addAction(Intent.ACTION_MEDIA_NOFS);
filter.addDataScheme("file");
registerReceiver(m_externalStorageReceiver, filter);
GetPathManager().StartExtStorageWatching(this, m_externalStorageReceiver);
updateExternalStorageState();
}
@ -1211,11 +1209,8 @@ public class MWMActivity extends NvEventQueueActivity
private void stopWatchingExternalStorage()
{
if (m_externalStorageReceiver != null)
{
unregisterReceiver(m_externalStorageReceiver);
m_externalStorageReceiver = null;
}
GetPathManager().StopExtStorageWatching();
m_externalStorageReceiver = null;
}

View file

@ -21,6 +21,7 @@ import com.mapswithme.maps.guides.GuideInfo;
import com.mapswithme.maps.guides.GuidesUtils;
import com.mapswithme.maps.location.LocationService;
import com.mapswithme.util.FbUtil;
import com.mapswithme.util.StoragePathManager;
import com.mapswithme.util.Utils;
import com.mapswithme.util.log.Logger;
import com.mapswithme.util.log.SimpleLogger;
@ -34,7 +35,7 @@ public class MWMApplication extends android.app.Application implements MapStorag
private static MWMApplication mSelf;
private StoragePathManager m_pathManager = new StoragePathManager();
private LocationService m_location = null;
private LocationState m_locationState = null;
private MapStorage m_storage = null;
@ -101,6 +102,11 @@ public class MWMApplication extends android.app.Application implements MapStorag
}
}
}
public StoragePathManager GetPathManager()
{
return m_pathManager;
}
@Override
public void onCountryProgress(Index idx, long current, long total)

View file

@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
import android.app.ActionBar;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@ -16,6 +17,7 @@ import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
@ -34,8 +36,8 @@ import com.mapswithme.util.statistics.Statistics;
public class SettingsActivity extends PreferenceActivity
{
private final static String ZOOM_BUTTON_ENABLED = "ZoomButtonsEnabled";
private native boolean isDownloadingActive();
private BroadcastReceiver m_receiver = null;
private Preference m_storagePreference = null;
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
@ -44,7 +46,6 @@ public class SettingsActivity extends PreferenceActivity
{
super.onCreate(savedInstanceState);
if (Utils.apiEqualOrGreaterThan(11))
{
// http://stackoverflow.com/questions/6867076/getactionbar-returns-null
@ -137,8 +138,23 @@ public class SettingsActivity extends PreferenceActivity
return true;
}
});
m_storagePreference = findPreference(getString(R.string.pref_storage_activity));
yotaSetup();
storagePathSetup();
}
@SuppressWarnings("deprecation")
private void storagePathSetup()
{
PreferenceScreen screen = getPreferenceScreen();
if (Yota.isYota())
screen.removePreference(m_storagePreference);
else if (MWMApplication.get().GetPathManager().HasMoreThanOnceStorage())
screen.addPreference(m_storagePreference);
else
screen.removePreference(m_storagePreference);
}
@SuppressWarnings("deprecation")
@ -158,9 +174,6 @@ public class SettingsActivity extends PreferenceActivity
return true;
}
});
// we dont allow to change maps location
getPreferenceScreen()
.removePreference(findPreference(getString(R.string.pref_storage_activity)));
}
}
@ -179,6 +192,29 @@ public class SettingsActivity extends PreferenceActivity
Statistics.INSTANCE.stopActivity(this);
}
@Override
protected void onResume()
{
super.onResume();
m_receiver = new BroadcastReceiver()
{
@Override
public void onReceive(Context context, Intent intent)
{
storagePathSetup();
}
};
MWMApplication.get().GetPathManager().StartExtStorageWatching(this, m_receiver);
}
@Override
protected void onPause()
{
super.onPause();
MWMApplication.get().GetPathManager().StopExtStorageWatching();
m_receiver = null;
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
@ -287,4 +323,6 @@ public class SettingsActivity extends PreferenceActivity
myWebView.loadUrl(url);
}
private native boolean isDownloadingActive();
}

View file

@ -21,6 +21,7 @@ import android.widget.CheckedTextView;
import android.widget.ListView;
import android.widget.TextView;
import com.mapswithme.maps.MWMApplication;
import com.mapswithme.maps.R;
import com.mapswithme.maps.base.MapsWithMeBaseListActivity;
import com.mapswithme.util.StoragePathManager;
@ -31,240 +32,9 @@ public class StoragePathActivity extends MapsWithMeBaseListActivity
{
private static String TAG = "StoragePathActivity";
/// ListView adapter
private static class StoragePathAdapter extends BaseAdapter
private StoragePathManager.StoragePathAdapter getAdapter()
{
private static String TAG = "StoragePathAdapter";
/// @name Different row types.
//@{
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private static final int TYPES_COUNT = 2;
//@}
private final LayoutInflater m_inflater;
private final Activity m_context;
private String m_currPath;
private final String m_defPath;
private long m_sizeNeeded;
private final int m_listItemHeight;
public StoragePathAdapter(Activity context, String currPath, String defPath)
{
m_context = context;
m_inflater = m_context.getLayoutInflater();
m_currPath = currPath;
m_defPath = defPath;
m_listItemHeight = (int)Utils.getAttributeDimension(context, android.R.attr.listPreferredItemHeight);
}
@SuppressLint("DefaultLocale")
private String getSizeString(long size)
{
final String arrS[] = { "Kb", "Mb", "Gb" };
long current = 1024;
int i = 0;
for (; i < arrS.length; ++i)
{
final long bound = 1024 * current;
if (size < bound)
break;
else
current = bound;
}
// left 1 digit after the comma and add postfix string
return String.format("%.1f %s", (double)size / (double)current, arrS[i]);
}
private List<StoragePathManager.StorageItem> m_items = new ArrayList<StoragePathManager.StorageItem>();
private int m_current = -1;
private boolean isAvailable(int index)
{
assert(index >= 0 && index < m_items.size());
return ((m_current != index) && (m_items.get(index).m_size >= m_sizeNeeded));
}
private int findItemByPath(String path)
{
for (int i = 0; i < m_items.size(); ++i)
if (m_items.get(i).m_path.equals(path))
return i;
return -1;
}
public void updateList()
{
m_sizeNeeded = StoragePathManager.getDirSize(m_currPath);
m_items = StoragePathManager.GetStorages(m_context, m_currPath, m_defPath);
// Find index of the current path.
m_current = findItemByPath(m_currPath);
assert(m_current != -1);
notifyDataSetChanged();
}
private static int HEADERS_COUNT = 1;
@Override
public int getItemViewType(int position)
{
return (position == 0 ? TYPE_HEADER : TYPE_ITEM);
}
@Override
public int getViewTypeCount()
{
return TYPES_COUNT;
}
@Override
public int getCount()
{
return (m_items != null ? m_items.size() + HEADERS_COUNT : HEADERS_COUNT);
}
@Override
public StoragePathManager.StorageItem getItem(int position)
{
return (position == 0 ? null : m_items.get(getIndexFromPos(position)));
}
@Override
public long getItemId(int position)
{
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
// 1. It's a strange thing, but when I tried to use setClickable,
// all the views become nonclickable.
// 2. I call setMinimumHeight(listPreferredItemHeight)
// because standard item's height is unknown.
switch (getItemViewType(position))
{
case TYPE_HEADER:
{
if (convertView == null)
{
convertView = m_inflater.inflate(android.R.layout.simple_list_item_1, null);
convertView.setMinimumHeight(m_listItemHeight);
}
final TextView v = (TextView) convertView;
v.setText(m_context.getString(R.string.maps) + ": " + getSizeString(m_sizeNeeded));
break;
}
case TYPE_ITEM:
{
final int index = getIndexFromPos(position);
final StoragePathManager.StorageItem item = m_items.get(index);
if (convertView == null)
{
convertView = m_inflater.inflate(android.R.layout.simple_list_item_single_choice, null);
convertView.setMinimumHeight(m_listItemHeight);
}
final CheckedTextView v = (CheckedTextView) convertView;
v.setText(item.m_path + ": " + getSizeString(item.m_size));
v.setChecked(index == m_current);
v.setEnabled((index == m_current) || isAvailable(index));
break;
}
}
return convertView;
}
private int getIndexFromPos(int position)
{
final int index = position - HEADERS_COUNT;
assert(index >= 0 && index < m_items.size());
return index;
}
private String getFullPath(int index)
{
assert(index >= 0 && index < m_items.size());
return StoragePathManager.getFullPath(m_items.get(index));
}
private void doUpdateAfterMove(String path)
{
m_currPath = path;
updateList();
}
public void onListItemClick(final int position)
{
final int index = getIndexFromPos(position);
if (isAvailable(index))
{
final String path = getFullPath(index);
final File f = new File(path);
if (!f.exists() && !f.mkdirs())
{
Log.e(TAG, "Can't create directory: " + path);
return;
}
new AlertDialog.Builder(m_context)
.setCancelable(false)
.setTitle(R.string.move_maps)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
{
Log.i(TAG, "Transfer data to storage: " + path);
StoragePathManager.StorageItem oldItem = null;
if (m_current != -1)
oldItem = m_items.get(m_current);
StoragePathManager.SetStoragePath(m_context, new StoragePathManager.SetStoragePathListener()
{
@Override
public void MoveFilesFinished(String newPath)
{
doUpdateAfterMove(newPath);
}
}, m_items.get(index), oldItem);
dlg.dismiss();
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
{
dlg.dismiss();
}
})
.create()
.show();
}
}
}
private StoragePathAdapter getAdapter()
{
return (StoragePathAdapter) getListView().getAdapter();
return (StoragePathManager.StoragePathAdapter)getListView().getAdapter();
}
@Override
@ -272,18 +42,7 @@ public class StoragePathActivity extends MapsWithMeBaseListActivity
{
super.onCreate(savedInstanceState);
final String currPath = nativeGetStoragePath();
final String defPath = Environment.getExternalStorageDirectory().getAbsolutePath();
Log.i(TAG, "Current and Default maps pathes: " + currPath + "; " + defPath);
setListAdapter(new StoragePathAdapter(this, currPath, defPath));
}
@Override
protected void onStart()
{
super.onStart();
getAdapter().updateList();
setListAdapter(MWMApplication.get().GetPathManager().GetAdapter());
}
@Override
@ -291,8 +50,20 @@ public class StoragePathActivity extends MapsWithMeBaseListActivity
{
// Do not process clicks on header items.
if (position != 0)
getAdapter().onListItemClick(position);
getAdapter().onItemClick(position);
}
@Override
protected void onResume()
{
super.onResume();
MWMApplication.get().GetPathManager().StartExtStorageWatching(this, null);
}
@Override
protected void onPause()
{
super.onPause();
MWMApplication.get().GetPathManager().StopExtStorageWatching();
}
private native String nativeGetStoragePath();
}

View file

@ -2,41 +2,62 @@ package com.mapswithme.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Array;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.List;
import java.util.Set;
import com.mapswithme.maps.Framework;
import com.mapswithme.maps.R;
import com.mapswithme.maps.settings.StoragePathActivity;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.StatFs;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckedTextView;
import android.widget.TextView;
public class StoragePathManager
{
private static String TAG = "StoragePathManager";
private static String MWM_DIR_POSTFIX = "/MapsWithMe/";
public static class StorageItem
{
public String m_path;
public long m_size;
public final String m_path;
public final long m_size;
StorageItem(String path, long size)
{
m_path = path;
m_size = size;
}
@Override
public boolean equals(Object o)
{
if (o == this) return true;
if (o == null) return false;
return m_size == ((StorageItem)o).m_size;
StorageItem other = (StorageItem)o;
return m_size == other.m_size || m_path.equals(other.m_size);
}
@Override
@ -46,21 +67,271 @@ public class StoragePathManager
}
}
static public ArrayList<StorageItem> GetStorages(Context context, String currentPath, String defPath)
public interface OnStorageItemClickListener
{
void onItemClick(int position);
}
public interface SetStoragePathListener
{
void MoveFilesFinished(String newPath);
void MoveFilesFailed();
}
public static class StoragePathAdapter extends BaseAdapter
implements OnStorageItemClickListener
{
@Override
public int getCount(){ return 0; }
@Override
public Object getItem(int position) { return null; }
@Override
public long getItemId(int position) { return 0; }
@Override
public View getView(int position, View convertView, ViewGroup parent) { return null; }
@Override
public void onItemClick(int position) {}
};
/// ListView adapter
private class StoragePathAdapterImpl extends StoragePathAdapter
{
private static final String TAG = "StoragePathAdapter";
/// @name Different row types.
//@{
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private static final int HEADERS_COUNT = 1;
private static final int TYPES_COUNT = 2;
//@}
private final LayoutInflater m_inflater;
private final Activity m_context;
private final int m_listItemHeight;
private List<StorageItem> m_items = null;
private int m_current = -1;
private long m_sizeNeeded;
public StoragePathAdapterImpl(Activity context)
{
m_context = context;
m_inflater = m_context.getLayoutInflater();
m_listItemHeight = (int)Utils.getAttributeDimension(context, android.R.attr.listPreferredItemHeight);
}
@Override
public int getItemViewType(int position)
{
return (position == 0 ? TYPE_HEADER : TYPE_ITEM);
}
@Override
public int getViewTypeCount()
{
return TYPES_COUNT;
}
@Override
public int getCount()
{
return (m_items != null ? m_items.size() + HEADERS_COUNT : HEADERS_COUNT);
}
@Override
public StorageItem getItem(int position)
{
return (position == 0 ? null : m_items.get(getIndexFromPos(position)));
}
@Override
public long getItemId(int position)
{
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
// 1. It's a strange thing, but when I tried to use setClickable,
// all the views become nonclickable.
// 2. I call setMinimumHeight(listPreferredItemHeight)
// because standard item's height is unknown.
switch (getItemViewType(position))
{
case TYPE_HEADER:
{
if (convertView == null)
{
convertView = m_inflater.inflate(android.R.layout.simple_list_item_1, null);
convertView.setMinimumHeight(m_listItemHeight);
}
final TextView v = (TextView) convertView;
v.setText(m_context.getString(R.string.maps) + ": " + getSizeString(m_sizeNeeded));
break;
}
case TYPE_ITEM:
{
final int index = getIndexFromPos(position);
final StorageItem item = m_items.get(index);
if (convertView == null)
{
convertView = m_inflater.inflate(android.R.layout.simple_list_item_single_choice, null);
convertView.setMinimumHeight(m_listItemHeight);
}
final CheckedTextView v = (CheckedTextView) convertView;
v.setText(item.m_path + ": " + getSizeString(item.m_size));
v.setChecked(index == m_current);
v.setEnabled((index == m_current) || isAvailable(index));
break;
}
}
return convertView;
}
@Override
public void onItemClick(int position)
{
final int index = getIndexFromPos(position);
if (isAvailable(index))
onStorageItemClick(index);
}
public void updateList(ArrayList<StorageItem> items, int currentItemIndex, long dirSize)
{
m_sizeNeeded = dirSize;
m_items = items;
m_current = currentItemIndex;
notifyDataSetChanged();
}
@SuppressLint("DefaultLocale")
private String getSizeString(long size)
{
final String arrS[] = { "Kb", "Mb", "Gb" };
long current = 1024;
int i = 0;
for (; i < arrS.length; ++i)
{
final long bound = 1024 * current;
if (size < bound)
break;
else
current = bound;
}
// left 1 digit after the comma and add postfix string
return String.format("%.1f %s", (double)size / (double)current, arrS[i]);
}
private boolean isAvailable(int index)
{
assert(index >= 0 && index < m_items.size());
return ((m_current != index) && (m_items.get(index).m_size >= m_sizeNeeded));
}
private int getIndexFromPos(int position)
{
final int index = position - HEADERS_COUNT;
assert(index >= 0 && index < m_items.size());
return index;
}
}
private static String TAG = "StoragePathManager";
private static String MWM_DIR_POSTFIX = "/MapsWithMe/";
private BroadcastReceiver m_externalListener;
private BroadcastReceiver m_internalListener;
private Activity m_context = null;
private ArrayList<StorageItem> m_items = null;
private StoragePathAdapterImpl m_adapter = null;
private int m_currentItemIndex = -1;
public void StartExtStorageWatching(Activity context, BroadcastReceiver listener)
{
m_context = context;
m_externalListener = listener;
m_internalListener = new BroadcastReceiver()
{
@Override
public void onReceive(Context context, Intent intent)
{
if (m_externalListener != null)
m_externalListener.onReceive(context, intent);
UpdateExternalStorages();
}
};
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
filter.addAction(Intent.ACTION_MEDIA_REMOVED);
filter.addAction(Intent.ACTION_MEDIA_EJECT);
filter.addAction(Intent.ACTION_MEDIA_SHARED);
filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
filter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL);
filter.addAction(Intent.ACTION_MEDIA_UNMOUNTABLE);
filter.addAction(Intent.ACTION_MEDIA_CHECKING);
filter.addAction(Intent.ACTION_MEDIA_NOFS);
filter.addDataScheme("file");
m_context.registerReceiver(m_internalListener, filter);
UpdateExternalStorages();
}
public StoragePathAdapter GetAdapter()
{
if (m_adapter == null)
{
m_adapter = new StoragePathAdapterImpl(m_context);
UpdateExternalStorages();
}
return m_adapter;
}
public void StopExtStorageWatching()
{
if (m_internalListener != null)
{
m_context.unregisterReceiver(m_internalListener);
m_internalListener = null;
m_externalListener = null;
m_adapter = null;
}
}
public boolean HasMoreThanOnceStorage()
{
return m_items.size() > 1;
}
public void UpdateExternalStorages()
{
ArrayList<String> pathes = new ArrayList<String>();
parseMountFile("/etc/vold.conf", VOLD_MODE, pathes);
parseMountFile("/etc/vold.fstab", VOLD_MODE, pathes);
parseMountFile("/system/etc/vold.fstab", VOLD_MODE, pathes);
parseMountFile("/proc/mounts", MOUNTS_MODE, pathes);
if (Utils.apiEqualOrGreaterThan(android.os.Build.VERSION_CODES.KITKAT))
{
File[] files = context.getExternalFilesDirs(null);
File[] files = m_context.getExternalFilesDirs(null);
if (files != null)
{
File primaryStorageDir = context.getExternalFilesDir(null);
File primaryStorageDir = m_context.getExternalFilesDir(null);
for(File f : files)
{
// On kitkat and Greater we ignore private folder on primary storage
@ -72,31 +343,43 @@ public class StoragePathManager
}
}
pathes.add(currentPath);
pathes.add(defPath);
ParseMountFile("/etc/vold.conf", VOLD_MODE, pathes);
ParseMountFile("/etc/vold.fstab", VOLD_MODE, pathes);
ParseMountFile("/system/etc/vold.fstab", VOLD_MODE, pathes);
ParseMountFile("/proc/mounts", MOUNTS_MODE, pathes);
ArrayList<StorageItem> items = new ArrayList<StorageItem>();
for (String path : pathes)
addStorage(path, items);
AddStorage(path, items);
return new ArrayList<StorageItem>(new LinkedHashSet<StorageItem>(items));
}
static public String getFullPath(StorageItem item)
{
return item.m_path + MWM_DIR_POSTFIX;
AddStorage(Environment.getExternalStorageDirectory().getAbsolutePath(), items);
String writableDir = GetWritableDirRoot();
StorageItem currentItem = AddStorage(writableDir, items);
LinkedHashSet<StorageItem> itemsSet = new LinkedHashSet<StorageItem>(items);
if (currentItem != null)
itemsSet.remove(currentItem);
m_items = new ArrayList<StorageItem>(itemsSet);
if (currentItem != null)
{
m_items.add(0, currentItem);
m_currentItemIndex = m_items.indexOf(currentItem);
}
else
m_currentItemIndex = -1;
if (m_adapter != null)
m_adapter.updateList(m_items, m_currentItemIndex, GetMWMDirSize());
}
/// @name Assume that MapsWithMe folder doesn't have inner folders and symbolic links.
//@{
static public long getDirSize(String basePath)
public long GetMWMDirSize()
{
return getDirSizeImpl(basePath + MWM_DIR_POSTFIX);
}
static private long getDirSizeImpl(String path)
{
final File dir = new File(path);
String writableDir = Framework.GetWritableDir();
final File dir = new File(writableDir);
assert(dir.exists());
assert(dir.isDirectory());
@ -106,19 +389,20 @@ public class StoragePathManager
assert(f.isFile());
size += f.length();
}
return (size + 1024*1024);
return size;
}
//@}
static public boolean MoveBookmarks()
public boolean MoveBookmarks()
{
ArrayList<String> pathes = new ArrayList<String>();
if (Utils.apiEqualOrGreaterThan(android.os.Build.VERSION_CODES.KITKAT))
{
parseMountFile("/etc/vold.conf", VOLD_MODE, pathes);
parseMountFile("/etc/vold.fstab", VOLD_MODE, pathes);
parseMountFile("/system/etc/vold.fstab", VOLD_MODE, pathes);
parseMountFile("/proc/mounts", MOUNTS_MODE, pathes);
ParseMountFile("/etc/vold.conf", VOLD_MODE, pathes);
ParseMountFile("/etc/vold.fstab", VOLD_MODE, pathes);
ParseMountFile("/system/etc/vold.fstab", VOLD_MODE, pathes);
ParseMountFile("/proc/mounts", MOUNTS_MODE, pathes);
}
ArrayList<String> approvedPathes = new ArrayList<String>();
@ -129,69 +413,297 @@ public class StoragePathManager
if (f.exists() || f.canRead() || f.isDirectory())
approvedPathes.add(mwmPath);
}
String tmp[] = approvedPathes.toArray(new String[approvedPathes.size()]);
return nativeMoveBookmarks(tmp, getFreeBytesAtPath(nativeGetBookmarkDir()));
final String settingsDir = Framework.GetSettingsDir();
final String writableDir = Framework.GetWritableDir();
final String bookmarkDir = Framework.GetBookmarksDir();
final String bookmarkFileExt = Framework.GetBookmarkFileExt();
LinkedHashSet<File> bookmarks = new LinkedHashSet<File>();
if (!settingsDir.equals(writableDir))
approvedPathes.add(writableDir);
for (String path : approvedPathes)
{
if (!path.equals(settingsDir))
AccamulateFiles(path, bookmarkFileExt, bookmarks);
}
long bookmarksSize = 0;
for (File f : bookmarks)
bookmarksSize += f.length();
if (GetFreeBytesAtPath(bookmarkDir) < bookmarksSize)
return false;
for (File f : bookmarks)
{
String name = f.getName();
name = name.replace(bookmarkFileExt, "");
name = nativeGenerateUniqueBookmarkName(name);
try
{
copyFile(f, new File(name));
f.delete();
}
catch(IOException e)
{
return false;
}
}
Framework.ReloadBookmarks();
return true;
}
static public boolean CheckWritableDir(Context context, SetStoragePathListener listener)
private void AccamulateFiles(final String dirPath, final String filesExtension, Set<File> result)
{
File f = new File(dirPath);
File[] bookmarks = f.listFiles(new FileFilter()
{
@Override
public boolean accept(File pathname)
{
return pathname.getName().endsWith(filesExtension);
}
});
result.addAll(Arrays.asList(bookmarks));
}
private void onStorageItemClick(int index)
{
final StorageItem oldItem = (m_currentItemIndex != -1) ? m_items.get(m_currentItemIndex) : null;
final StorageItem item = m_items.get(index);
final String path = GetItemFullPath(item);
final File f = new File(path);
if (!f.exists() && !f.mkdirs())
{
Log.e(TAG, "Can't create directory: " + path);
return;
}
new AlertDialog.Builder(m_context)
.setCancelable(false)
.setTitle(R.string.move_maps)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
{
SetStoragePathImpl(m_context, new StoragePathManager.SetStoragePathListener()
{
@Override
public void MoveFilesFinished(String newPath)
{
UpdateExternalStorages();
}
@Override
public void MoveFilesFailed()
{
UpdateExternalStorages();
}
}, item, oldItem, R.string.wait_several_minutes);
dlg.dismiss();
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dlg, int which)
{
dlg.dismiss();
}
})
.create()
.show();
}
public void CheckWritableDir(Context context, SetStoragePathListener listener)
{
if (Utils.apiLowerThan(android.os.Build.VERSION_CODES.KITKAT))
return true;
return;
final String settingsDir = nativeGetSettingsDir();
final String writableDir = nativeGetWritableDir();
final String settingsDir = Framework.GetSettingsDir();
final String writableDir = Framework.GetWritableDir();
if (settingsDir.equals(writableDir))
return true;
return;
if (isDirWritable(writableDir))
return true;
if (IsDirWritable(writableDir))
return;
final ArrayList<StorageItem> items = GetStorages(context, writableDir.replace(MWM_DIR_POSTFIX, ""),
settingsDir.replace(MWM_DIR_POSTFIX, ""));
final long size = getDirSizeImpl(writableDir);
for (StorageItem item : items)
final long size = GetMWMDirSize();
for (StorageItem item : m_items)
{
if (item.m_size > size)
{
SetStoragePathImpl(context, listener, item, null, R.string.kitkat_optimization_in_progress);
return true;
SetStoragePathImpl(context, listener, item, new StorageItem(GetWritableDirRoot(), 0), R.string.kitkat_optimization_in_progress);
return;
}
}
return false;
listener.MoveFilesFailed();
}
public interface SetStoragePathListener
{
void MoveFilesFinished(String newPath);
}
static public void SetStoragePath(Context context, SetStoragePathListener listener, StorageItem newStorage, StorageItem oldStorage)
{
SetStoragePathImpl(context, listener, newStorage, oldStorage, R.string.wait_several_minutes);
}
static private void SetStoragePathImpl(Context context, SetStoragePathListener listener,
StorageItem newStorage, StorageItem oldStorage,
int messageId)
private void SetStoragePathImpl(Context context, SetStoragePathListener listener,
StorageItem newStorage, StorageItem oldStorage, int messageId)
{
MoveFilesTask task = new MoveFilesTask(context, listener, newStorage, oldStorage, messageId);
task.execute("");
}
static private boolean isDirWritable(String path)
static private boolean DoMoveMaps(StorageItem newStorage, StorageItem oldStorage)
{
final File f = new File(path + "/testDir");
f.mkdir();
// we can't only call canWrite, because on KitKat (Samsung S4) this return true
// for sdcard but actually it's read only
if (f.exists())
String fullOldPath = GetItemFullPath(oldStorage);
String fullNewPath = GetItemFullPath(newStorage);
File oldDir = new File(fullOldPath);
File newDir = new File(fullNewPath);
if (!newDir.exists())
newDir.mkdir();
assert(IsDirWritable(fullNewPath));
assert(newDir.isDirectory());
assert(oldDir.isDirectory());
final String[] extensions = Framework.GetMovableFilesExt();
File[] internalFiles = oldDir.listFiles(new FileFilter()
{
f.delete();
return true;
@Override
public boolean accept(File pathname)
{
for (String postfix : extensions)
{
if (pathname.getName().endsWith(postfix))
return true;
}
return false;
}
});
try
{
for (File moveFile : internalFiles)
copyFile(moveFile, new File(fullNewPath + moveFile.getName()));
}
catch (IOException e)
{
for (File moveFile : internalFiles)
new File(fullNewPath + moveFile.getName()).delete();
return false;
}
return false;
Framework.SetWritableDir(fullNewPath);
for (File moveFile : internalFiles)
moveFile.delete();
return true;
}
private static void copyFile(File source, File dest) throws IOException
{
FileChannel inputChannel = null;
FileChannel outputChannel = null;
try
{
inputChannel = new FileInputStream(source).getChannel();
outputChannel = new FileOutputStream(dest).getChannel();
outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
}
finally
{
inputChannel.close();
outputChannel.close();
}
}
private class MoveFilesTask extends AsyncTask<String, Void, Boolean>
{
private final ProgressDialog m_dlg;
private final StorageItem m_newStorage;
private final StorageItem m_oldStorage;
private final SetStoragePathListener m_listener;
public MoveFilesTask(Context context, SetStoragePathListener listener,
StorageItem newStorage, StorageItem oldStorage, int messageID)
{
m_newStorage = newStorage;
m_oldStorage = oldStorage;
m_listener = listener;
m_dlg = new ProgressDialog(context);
m_dlg.setMessage(context.getString(messageID));
m_dlg.setProgressStyle(ProgressDialog.STYLE_SPINNER);
m_dlg.setIndeterminate(true);
m_dlg.setCancelable(false);
}
@Override
protected void onPreExecute()
{
m_dlg.show();
}
@Override
protected Boolean doInBackground(String... params)
{
return DoMoveMaps(m_newStorage, m_oldStorage);
}
@Override
protected void onPostExecute(Boolean result)
{
// Using dummy try-catch because of the following:
// http://stackoverflow.com/questions/2745061/java-lang-illegalargumentexception-view-not-attached-to-window-manager
try
{
m_dlg.dismiss();
}
catch (final Exception e)
{
}
if (result)
m_listener.MoveFilesFinished(m_newStorage.m_path);
else
m_listener.MoveFilesFailed();
UpdateExternalStorages();
}
}
static private StorageItem AddStorage(String path, ArrayList<StorageItem> items)
{
try
{
final File f = new File(path + "/");
if (f.exists() && f.isDirectory() && f.canWrite())
{
if (!IsDirWritable(path))
return null;
final long size = GetFreeBytesAtPath(path);
final StorageItem item = new StorageItem(path, size);
items.add(item);
return item;
}
else
Log.i(TAG, "File error for storage: " + path);
}
catch (final IllegalArgumentException ex)
{
// Suppress exceptions for unavailable storages.
Log.i(TAG, "StatFs error for storage: " + path);
}
return null;
}
private static int VOLD_MODE = 1;
@ -200,7 +712,7 @@ public class StoragePathManager
// 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 private void parseMountFile(String file, int mode, ArrayList<String> pathes)
static private void ParseMountFile(String file, int mode, ArrayList<String> pathes)
{
Log.i(TAG, "Parsing " + file);
@ -257,46 +769,9 @@ public class StoragePathManager
}
}
private static boolean addStorage(String path, ArrayList<StorageItem> items)
{
try
{
final File f = new File(path + "/");
if (f.exists() && f.isDirectory() && f.canWrite())
{
if (!isDirWritable(path))
return false;
for (StorageItem item : items)
{
if (item.m_path.equals(path))
return true;
}
final long size = getFreeBytesAtPath(path);
final StorageItem item = new StorageItem();
item.m_path = path;
item.m_size = size;
items.add(item);
return true;
}
else
Log.i(TAG, "File error for storage: " + path);
}
catch (final IllegalArgumentException ex)
{
// Suppress exceptions for unavailable storages.
Log.i(TAG, "StatFs error for storage: " + path);
}
return false;
}
@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
static private long getFreeBytesAtPath(String path)
static private long GetFreeBytesAtPath(String path)
{
final StatFs stat = new StatFs(path);
final long size = Utils.apiLowerThan(android.os.Build.VERSION_CODES.JELLY_BEAN_MR2)
@ -305,105 +780,35 @@ public class StoragePathManager
return size;
}
static private boolean doMoveMaps(StorageItem newStorage, StorageItem oldStorage)
static private boolean IsDirWritable(String path)
{
String fullNewPath = getFullPath(newStorage);
File f = new File(fullNewPath);
if (!f.exists())
f.mkdir();
assert(f.canWrite());
assert(f.isDirectory());
if (StoragePathManager.nativeSetStoragePath(fullNewPath))
final File f = new File(path + "/testDir");
f.mkdir();
// we can't only call canWrite, because on KitKat (Samsung S4) this return true
// for sdcard but actually it's read only
if (f.exists())
{
if (oldStorage != null)
deleteFiles(new File(getFullPath(oldStorage)));
f.delete();
return true;
}
return false;
}
//delete all files (except settings.ini) in directory and bookmarks
static private void deleteFiles(File dir)
static private String GetItemFullPath(StorageItem item)
{
assert(dir.exists());
assert(dir.isDirectory());
for (final File file : dir.listFiles())
{
assert(file.isFile());
// skip settings.ini - this file should be always in one place
if (file.getName().equalsIgnoreCase("settings.ini"))
continue;
// skip bookmarks
if (file.getName().endsWith("kml"))
continue;
if (!file.delete())
Log.w(TAG, "Can't delete file: " + file.getName());
}
}
//@}
private static class MoveFilesTask extends AsyncTask<String, Void, Boolean>
{
private final ProgressDialog m_dlg;
private final StorageItem m_newStorage;
private final StorageItem m_oldStorage;
private final SetStoragePathListener m_listener;
public MoveFilesTask(Context context, SetStoragePathListener listener,
StorageItem newStorage, StorageItem oldStorage, int messageID)
{
m_newStorage = newStorage;
m_oldStorage = oldStorage;
m_listener = listener;
m_dlg = new ProgressDialog(context);
m_dlg.setMessage(context.getString(messageID));
m_dlg.setProgressStyle(ProgressDialog.STYLE_SPINNER);
m_dlg.setIndeterminate(true);
m_dlg.setCancelable(false);
}
@Override
protected void onPreExecute()
{
m_dlg.show();
}
@Override
protected Boolean doInBackground(String... params)
{
return doMoveMaps(m_newStorage, m_oldStorage);
}
@Override
protected void onPostExecute(Boolean result)
{
// Using dummy try-catch because of the following:
// http://stackoverflow.com/questions/2745061/java-lang-illegalargumentexception-view-not-attached-to-window-manager
try
{
m_dlg.dismiss();
}
catch (final Exception e)
{
}
if (result)
m_listener.MoveFilesFinished(m_newStorage.m_path);
}
return item.m_path + MWM_DIR_POSTFIX;
}
static private native boolean nativeMoveBookmarks(String[] additionalFindPathes, long availableSize);
static private native boolean nativeSetStoragePath(String newPath);
static private native String nativeGetBookmarkDir();
static private native String nativeGetSettingsDir();
static private native String nativeGetWritableDir();
static private String GetWritableDirRoot()
{
String writableDir = Framework.GetWritableDir();
int index = writableDir.lastIndexOf(MWM_DIR_POSTFIX);
if (index != -1)
writableDir = writableDir.substring(0, index);
return writableDir;
}
static private native String nativeGenerateUniqueBookmarkName(String baseName);
}