Merge pull request #3703 from mgsergio/editor-localized-name

[android] Editor localized name
This commit is contained in:
Alexander Marchuk 2016-07-05 13:02:13 +03:00 committed by GitHub
commit 9e3b811ff0
16 changed files with 247 additions and 82 deletions

View file

@ -4,7 +4,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
classpath 'com.android.tools.build:gradle:2.1.2'
}
}

View file

@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
classpath 'com.android.tools.build:gradle:2.1.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View file

@ -7,7 +7,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
classpath 'com.android.tools.build:gradle:2.1.2'
classpath 'io.fabric.tools:gradle:1.+'
}
}

View file

@ -51,20 +51,28 @@ jobject GetNewParcelablePointD(JNIEnv * env, m2::PointD const & point);
jobject GetNewPoint(JNIEnv * env, m2::PointD const & point);
jobject GetNewPoint(JNIEnv * env, m2::PointI const & point);
template<typename TValue, typename TToJavaFn>
jobjectArray ToJavaArray(JNIEnv * env, jclass clazz, vector<TValue> const & src, TToJavaFn && toJavaFn)
template<typename TIt, typename TToJavaFn>
jobjectArray ToJavaArray(JNIEnv * env, jclass clazz, TIt begin, TIt end, size_t const size, TToJavaFn && toJavaFn)
{
size_t const size = src.size();
jobjectArray jArray = env->NewObjectArray((jint) size, clazz, 0);
for (size_t i = 0; i < size; i++)
size_t i = 0;
for (auto it = begin; it != end; ++it)
{
TScopedLocalRef jItem(env, toJavaFn(env, src[i]));
TScopedLocalRef jItem(env, toJavaFn(env, *it));
env->SetObjectArrayElement(jArray, i, jItem.get());
++i;
}
return jArray;
}
template<typename TContainer, typename TToJavaFn>
jobjectArray ToJavaArray(JNIEnv * env, jclass clazz, TContainer const & src, TToJavaFn && toJavaFn)
{
return ToJavaArray(env, clazz, begin(src), end(src), src.size(), forward<TToJavaFn>(toJavaFn));
}
jobjectArray ToJavaStringArray(JNIEnv * env, vector<string> const & src);
void DumpDalvikReferenceTables();
}
} // namespace jni

View file

@ -3,6 +3,8 @@
#include "com/mapswithme/core/jni_helper.hpp"
#include "com/mapswithme/maps/Framework.hpp"
#include "coding/multilang_utf8_string.hpp"
#include "base/assert.hpp"
#include "base/logging.hpp"
#include "base/string_utils.hpp"
@ -344,13 +346,18 @@ Java_com_mapswithme_maps_editor_Editor_nativeGetNearbyStreets(JNIEnv * env, jcla
JNIEXPORT jobjectArray JNICALL
Java_com_mapswithme_maps_editor_Editor_nativeGetSupportedLanguages(JNIEnv * env, jclass clazz)
{
// TODO (yunikkk) implement
using TLang = StringUtf8Multilang::Lang;
//public Language(@NonNull String code, @NonNull String name)
// static jclass const langClass = jni::GetGlobalClassRef(env, "com/mapswithme/maps/editor/data/Language");
// static jmethodID const langCtor = jni::GetConstructorID(env, langClass, "(Ljava/lang/String;Ljava/lang/String;)V");
static jclass const langClass = jni::GetGlobalClassRef(env, "com/mapswithme/maps/editor/data/Language");
static jmethodID const langCtor = jni::GetConstructorID(env, langClass, "(Ljava/lang/String;Ljava/lang/String;)V");
// return jni::ToJavaArray(env, langClass, g_editableMapObject.GetBuildingLevels(), );
return nullptr;
return jni::ToJavaArray(env, langClass, StringUtf8Multilang::GetSupportedLanguages(),
[](JNIEnv * env, TLang const & lang)
{
jni::TScopedLocalRef const code(env, jni::ToJavaString(env, lang.m_code));
jni::TScopedLocalRef const name(env, jni::ToJavaString(env, lang.m_name));
return env->NewObject(langClass, langCtor, code.get(), name.get());
});
}
JNIEXPORT jstring JNICALL
@ -583,4 +590,12 @@ Java_com_mapswithme_maps_editor_Editor_nativeIsMapObjectUploaded(JNIEnv * env, j
{
return osm::Editor::Instance().IsFeatureUploaded(g_editableMapObject.GetID().m_mwmId, g_editableMapObject.GetID().m_index);
}
// static nativeMakeLocalizedName(String langCode, String name);
JNIEXPORT jobject JNICALL
Java_com_mapswithme_maps_editor_Editor_nativeMakeLocalizedName(JNIEnv * env, jclass clazz, jstring code, jstring name)
{
osm::LocalizedName localizedName(jni::ToNativeString(env, code), jni::ToNativeString(env, name));
return ToJavaName(env, localizedName);
}
} // extern "C"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 B

View file

@ -105,6 +105,7 @@ public final class Editor
public static native void nativeSetDefaultName(String name);
public static native @NonNull LocalizedName[] nativeGetLocalizedNames();
public static native void nativeSetLocalizedNames(@NonNull LocalizedName[] names);
public static native LocalizedName nativeMakeLocalizedName(String langCode, String name);
public static native Language[] nativeGetSupportedLanguages();
public static native LocalizedStreet nativeGetStreet();

View file

@ -17,6 +17,7 @@ import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
@ -37,6 +38,8 @@ import org.solovyev.android.views.llm.LinearLayoutManager;
public class EditorFragment extends BaseMwmFragment implements View.OnClickListener, EditTextDialogFragment.OnTextSaveListener
{
final static String LAST_LOCALIZED_NAME_INDEX = "LastLocalizedNameIndex";
private TextView mCategory;
private View mCardName;
private View mCardAddress;
@ -44,6 +47,7 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
private EditText mName;
private RecyclerView mLocalizedNames;
private final RecyclerView.AdapterDataObserver mLocalizedNamesObserver = new RecyclerView.AdapterDataObserver()
{
@Override
@ -76,6 +80,7 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
refreshLocalizedNames();
}
};
private MultilanguageAdapter mLocalizedNamesAdapter;
private TextView mLocalizedShow;
private boolean mIsLocalizedShown;
@ -195,6 +200,7 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
refreshOpeningTime();
refreshEditableFields();
refreshResetButton();
refreshLocalizedNames();
}
@Override
@ -218,7 +224,7 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
Editor.nativeSetEmail(mEmail.getText().toString());
Editor.nativeSetHasWifi(mWifi.isChecked());
Editor.nativeSetOperator(mOperator.getText().toString());
// TODO set localizated names
Editor.nativeSetLocalizedNames(mParent.getLocalizedNamesAsArray());
return true;
}
@ -324,6 +330,47 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
}
}
private void initLocalizedNameView(final View view)
{
mLocalizedNames = (RecyclerView) view.findViewById(R.id.recycler);
mLocalizedNames.setNestedScrollingEnabled(false);
mLocalizedNames.setLayoutManager(new LinearLayoutManager(getActivity()));
mLocalizedNamesAdapter = new MultilanguageAdapter(mParent);
mLocalizedNames.setAdapter(mLocalizedNamesAdapter);
mLocalizedNamesAdapter.registerAdapterDataObserver(mLocalizedNamesObserver);
refreshLocalizedNames();
final Bundle args = getArguments();
if (args == null || !args.containsKey(LAST_LOCALIZED_NAME_INDEX))
{
showLocalizedNames(false);
return;
}
showLocalizedNames(true);
UiUtils.waitLayout(mLocalizedNames, new ViewTreeObserver.OnGlobalLayoutListener()
{
@Override
public void onGlobalLayout()
{
LinearLayoutManager lm = (LinearLayoutManager) mLocalizedNames.getLayoutManager();
int position = args.getInt(LAST_LOCALIZED_NAME_INDEX);
View nameItem = lm.findViewByPosition(position);
int cvNameTop = view.findViewById(R.id.cv__name).getTop();
int nameItemTop = nameItem.getTop();
view.scrollTo(0, cvNameTop + nameItemTop);
// TODO(mgsergio): Uncomment if focus and keyboard are required.
// TODO(mgsergio): Keyboard doesn't want to hide. Only pressing back button works.
// View nameItemInput = nameItem.findViewById(R.id.input);
// nameItemInput.requestFocus();
// InputUtils.showKeyboard(nameItemInput);
}
});
}
private void initViews(View view)
{
final View categoryBlock = view.findViewById(R.id.category);
@ -335,18 +382,10 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
mCardAddress = view.findViewById(R.id.cv__address);
mCardMetadata = view.findViewById(R.id.cv__metadata);
mName = findInput(mCardName);
// TODO uncomment and finish localized name
// view.findViewById(R.id.add_langs).setOnClickListener(this);
UiUtils.hide(view.findViewById(R.id.add_langs));
view.findViewById(R.id.add_langs).setOnClickListener(this);
mLocalizedShow = (TextView) view.findViewById(R.id.show_langs);
mLocalizedShow.setOnClickListener(this);
mLocalizedNames = (RecyclerView) view.findViewById(R.id.recycler);
mLocalizedNames.setLayoutManager(new LinearLayoutManager(getActivity()));
mLocalizedNamesAdapter = new MultilanguageAdapter(Editor.nativeGetLocalizedNames());
mLocalizedNames.setAdapter(mLocalizedNamesAdapter);
mLocalizedNamesAdapter.registerAdapterDataObserver(mLocalizedNamesObserver);
refreshLocalizedNames();
showLocalizedNames(false);
initLocalizedNameView(view);
// Address
view.findViewById(R.id.block_street).setOnClickListener(this);
@ -462,9 +501,7 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
private void refreshLocalizedNames()
{
// TODO uncomment and finish localized names
// UiUtils.showIf(mLocalizedNamesAdapter.getItemCount() > 0, mLocalizedShow);
UiUtils.hide(mLocalizedNames, mLocalizedShow);
UiUtils.showIf(mLocalizedNamesAdapter.getItemCount() > 0, mLocalizedShow);
}
private void showLocalizedNames(boolean show)

View file

@ -16,6 +16,8 @@ import com.mapswithme.maps.MwmActivity;
import com.mapswithme.maps.R;
import com.mapswithme.maps.base.BaseMwmToolbarFragment;
import com.mapswithme.maps.base.OnBackPressListener;
import com.mapswithme.maps.editor.data.Language;
import com.mapswithme.maps.editor.data.LocalizedName;
import com.mapswithme.maps.editor.data.LocalizedStreet;
import com.mapswithme.maps.widget.SearchToolbarController;
import com.mapswithme.maps.widget.ToolbarController;
@ -24,8 +26,11 @@ import com.mapswithme.util.UiUtils;
import com.mapswithme.util.Utils;
import com.mapswithme.util.statistics.Statistics;
import java.util.ArrayList;
import java.util.List;
public class EditorHostFragment extends BaseMwmToolbarFragment
implements OnBackPressListener, View.OnClickListener
implements OnBackPressListener, View.OnClickListener, LanguagesFragment.Listener
{
private boolean mIsNewObject;
@ -40,6 +45,48 @@ public class EditorHostFragment extends BaseMwmToolbarFragment
private Mode mMode;
/**
* A list of localized names added by a user and those that were in metadata.
*/
private static final List<LocalizedName> sLocalizedNames = new ArrayList<>();
/**
* Used in MultilanguageAdapter to show, select and remove items.
*/
List<LocalizedName> getLocalizedNames()
{
return sLocalizedNames;
}
public LocalizedName[] getLocalizedNamesAsArray()
{
return sLocalizedNames.toArray(new LocalizedName[sLocalizedNames.size()]);
}
void setLocalizedNames(LocalizedName[] names)
{
sLocalizedNames.clear();
for (LocalizedName name : names)
{
if (name.code == LocalizedName.DEFAULT_LANG_CODE)
continue;
sLocalizedNames.add(name);
}
}
/**
* Sets .name of an index item to name.
*/
void setName(String name, int index)
{
sLocalizedNames.get(index).name = name;
}
void addLocalizedName(LocalizedName name)
{
sLocalizedNames.add(name);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
@ -65,6 +112,7 @@ public class EditorHostFragment extends BaseMwmToolbarFragment
mIsNewObject = getArguments().getBoolean(EditorActivity.EXTRA_NEW_OBJECT, false);
mToolbarController.setTitle(getTitle());
setLocalizedNames(Editor.nativeGetLocalizedNames());
editMapObject();
}
@ -104,11 +152,19 @@ public class EditorHostFragment extends BaseMwmToolbarFragment
}
protected void editMapObject()
{
editMapObject(false /* focusToLastLocalizedName */);
}
protected void editMapObject(boolean focusToLastLocalizedName)
{
mMode = Mode.MAP_OBJECT;
((SearchToolbarController) mToolbarController).showControls(false);
mToolbarController.setTitle(getTitle());
final Fragment editorFragment = Fragment.instantiate(getActivity(), EditorFragment.class.getName());
Bundle args = new Bundle();
if (focusToLastLocalizedName)
args.putInt(EditorFragment.LAST_LOCALIZED_NAME_INDEX, sLocalizedNames.size() - 1);
final Fragment editorFragment = Fragment.instantiate(getActivity(), EditorFragment.class.getName(), args);
getChildFragmentManager().beginTransaction()
.replace(R.id.fragment_container, editorFragment, EditorFragment.class.getName())
.commit();
@ -133,7 +189,12 @@ public class EditorHostFragment extends BaseMwmToolbarFragment
protected void addLocalizedLanguage()
{
editWithFragment(Mode.LANGUAGE, R.string.choose_language, null, LanguagesFragment.class, false);
Bundle args = new Bundle();
ArrayList<String> languages = new ArrayList<>(sLocalizedNames.size());
for (LocalizedName name : sLocalizedNames)
languages.add(name.lang);
args.putStringArrayList(LanguagesFragment.EXISTING_LOCALIZED_NAMES, languages);
editWithFragment(Mode.LANGUAGE, R.string.choose_language, args, LanguagesFragment.class, false);
}
private void editWithFragment(Mode newMode, @StringRes int toolbarTitle, @Nullable Bundle args, Class<? extends Fragment> fragmentClass, boolean showSearch)
@ -249,4 +310,11 @@ public class EditorHostFragment extends BaseMwmToolbarFragment
{
return mIsNewObject;
}
@Override
public void onLanguageSelected(Language lang)
{
addLocalizedName(Editor.nativeMakeLocalizedName(lang.code, ""));
editMapObject(true /* focusToLastLocalizedName */);
}
}

View file

@ -1,12 +1,24 @@
package com.mapswithme.maps.editor;
import android.os.Bundle;
import android.support.v7.widget.RecyclerView;
import com.mapswithme.maps.base.BaseMwmRecyclerFragment;
import com.mapswithme.maps.editor.data.Language;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class LanguagesFragment extends BaseMwmRecyclerFragment
{
final static String EXISTING_LOCALIZED_NAMES = "ExistingLocalizedNames";
public interface Listener
{
void onLanguageSelected(Language language);
@ -15,14 +27,30 @@ public class LanguagesFragment extends BaseMwmRecyclerFragment
@Override
protected RecyclerView.Adapter createAdapter()
{
return new LanguagesAdapter(this, Editor.nativeGetSupportedLanguages());
Bundle args = getArguments();
Set<String> existingLanguages = new HashSet<>(args.getStringArrayList(EXISTING_LOCALIZED_NAMES));
List<Language> languages = new ArrayList<>();
for (Language lang : Editor.nativeGetSupportedLanguages())
{
if (!existingLanguages.contains(lang.code))
languages.add(lang);
}
Collections.sort(languages, new Comparator<Language>()
{
@Override
public int compare(Language lhs, Language rhs) {
return lhs.name.compareTo(rhs.name);
}
});
return new LanguagesAdapter(this, languages.toArray(new Language[languages.size()]));
}
protected void onLanguageSelected(Language language)
{
if (getActivity() instanceof Listener)
((Listener) getActivity()).onLanguageSelected(language);
else if (getParentFragment() instanceof Listener)
if (getParentFragment() instanceof Listener)
((Listener) getParentFragment()).onLanguageSelected(language);
}
}

View file

@ -1,9 +1,6 @@
package com.mapswithme.maps.editor;
import android.support.v7.util.SortedList;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.util.SortedListAdapterCallback;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -11,55 +8,29 @@ import android.widget.EditText;
import com.mapswithme.maps.R;
import com.mapswithme.maps.editor.data.LocalizedName;
import com.mapswithme.util.StringUtils;
import com.mapswithme.util.UiUtils;
import java.util.List;
public class MultilanguageAdapter extends RecyclerView.Adapter<MultilanguageAdapter.Holder>
{
private SortedList<LocalizedName> mNames;
private final List<LocalizedName> mNames;
private EditorHostFragment mHostFragment;
MultilanguageAdapter(LocalizedName[] names)
// TODO(mgsergio): Refactor: don't pass the whole EditorHostFragment.
MultilanguageAdapter(EditorHostFragment hostFragment)
{
mNames = new SortedList<>(LocalizedName.class,
new SortedListAdapterCallback<LocalizedName>(this)
{
@Override
public int compare(LocalizedName o1, LocalizedName o2)
{
return o1.lang.compareTo(o2.lang);
}
@Override
public boolean areContentsTheSame(LocalizedName oldItem, LocalizedName newItem)
{
return TextUtils.equals(oldItem.name, newItem.name);
}
@Override
public boolean areItemsTheSame(LocalizedName item1, LocalizedName item2)
{
return item1.code == item2.code;
}
},
names.length);
// skip default name
for (int i = 1; i < names.length; i++)
mNames.add(names[i]);
}
public void setNames(SortedList<LocalizedName> names)
{
mNames = names;
}
public SortedList<LocalizedName> getNames()
{
return mNames;
mHostFragment = hostFragment;
mNames = hostFragment.getLocalizedNames();
}
@Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType)
{
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_localized_name, parent, false);
// TODO(mgsergio): Deletion is not implemented.
UiUtils.hide(view.findViewById(R.id.delete));
return new Holder(view);
}
@ -85,12 +56,25 @@ public class MultilanguageAdapter extends RecyclerView.Adapter<MultilanguageAdap
{
super(itemView);
input = (EditText) itemView.findViewById(R.id.input);
input.addTextChangedListener(new StringUtils.SimpleTextWatcher()
{
@Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
mHostFragment.setName(s.toString(), getAdapterPosition());
}
});
itemView.findViewById(R.id.delete).setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
mNames.removeItemAt(getAdapterPosition());
// TODO(mgsergio): Implement item deletion.
// int position = getAdapterPosition();
// mHostFragment.removeLocalizedName(position + 1);
// mNames.remove(position);
// notifyItemRemoved(position);
}
});
}

View file

@ -4,6 +4,9 @@ import android.support.annotation.NonNull;
public class LocalizedName
{
// StringUtf8Multilang::kDefaultCode.
public static final int DEFAULT_LANG_CODE = 0;
public int code;
@NonNull public String name;
@NonNull public String lang;

View file

@ -11,6 +11,26 @@
namespace osm
{
// LocalizedName -----------------------------------------------------------------------------------
LocalizedName::LocalizedName(int8_t const code, string const & name)
: m_code(code)
, m_lang(StringUtf8Multilang::GetLangByCode(code))
, m_langName(StringUtf8Multilang::GetLangNameByCode(code))
, m_name(name)
{
}
LocalizedName::LocalizedName(string const & langCode, string const & name)
: m_code(StringUtf8Multilang::GetLangIndex(langCode))
, m_lang(StringUtf8Multilang::GetLangByCode(m_code))
, m_langName(StringUtf8Multilang::GetLangNameByCode(m_code))
, m_name(name)
{
}
// EditableMapObject -------------------------------------------------------------------------------
// static
int8_t const EditableMapObject::kMaximumLevelsEditableByUsers = 25;
@ -34,8 +54,7 @@ vector<LocalizedName> EditableMapObject::GetLocalizedNames() const
vector<LocalizedName> result;
m_name.ForEach([&result](int8_t code, string const & name) -> bool
{
result.push_back({code, StringUtf8Multilang::GetLangByCode(code),
StringUtf8Multilang::GetLangNameByCode(code), name});
result.push_back({code, name});
return true;
});
return result;
@ -212,7 +231,7 @@ bool EditableMapObject::ValidateBuildingLevels(string const & buildingLevels)
// static
bool EditableMapObject::ValidateHouseNumber(string const & houseNumber)
{
// TODO(mgsergio): Make a better validation, use real samples for example.
// TODO(mgsergio): Use LooksLikeHouseNumber!
if (houseNumber.empty())
return true;

View file

@ -35,6 +35,9 @@ struct EditableProperties
struct LocalizedName
{
LocalizedName(int8_t code, string const & name);
LocalizedName(string const & langCode, string const & name);
// m_code, m_lang and m_langName are defined in StringUtf8Multilang.
int8_t const m_code;
// Non-owning pointers to internal static char const * array.

View file

@ -90,8 +90,7 @@ vector<osm::LocalizedName> getAdditionalLocalizedNames(osm::EditableMapObject co
emo.GetName().ForEach([&result](int8_t code, string const & name) -> bool
{
if (code != StringUtf8Multilang::kDefaultCode)
result.push_back({code, StringUtf8Multilang::GetLangByCode(code),
StringUtf8Multilang::GetLangNameByCode(code), name});
result.push_back({code, name});
return true;
});
return result;