[Android] Standalone Note UI in Category select screen

Signed-off-by: hemanggs <hemangmanhas@gmail.com>
This commit is contained in:
hemanggs
2025-06-06 17:49:35 +05:30
committed by Konstantin Pastbin
parent 9e494ed8a5
commit 2492e8bda4
6 changed files with 155 additions and 16 deletions

View File

@@ -7,8 +7,12 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.textfield.TextInputEditText;
import app.organicmaps.R;
import app.organicmaps.sdk.editor.data.FeatureCategory;
import app.organicmaps.sdk.util.StringUtils;
import app.organicmaps.sdk.util.UiUtils;
import com.google.android.material.textview.MaterialTextView;
@@ -21,6 +25,12 @@ public class FeatureCategoryAdapter extends RecyclerView.Adapter<RecyclerView.Vi
private final FeatureCategoryFragment mFragment;
private final FeatureCategory mSelectedCategory;
public interface FooterListener
{
void onNoteTextChanged(String newText);
void onSendNoteClicked();
}
public FeatureCategoryAdapter(@NonNull FeatureCategoryFragment host, @NonNull FeatureCategory[] categories,
@Nullable FeatureCategory category)
{
@@ -57,7 +67,7 @@ public class FeatureCategoryAdapter extends RecyclerView.Adapter<RecyclerView.Vi
case TYPE_FOOTER ->
{
return new FooterViewHolder(
LayoutInflater.from(parent.getContext()).inflate(R.layout.item_feature_category_footer, parent, false));
LayoutInflater.from(parent.getContext()).inflate(R.layout.item_feature_category_footer, parent, false), (FooterListener) mFragment);
}
default -> throw new IllegalArgumentException("Unsupported");
}
@@ -70,6 +80,10 @@ public class FeatureCategoryAdapter extends RecyclerView.Adapter<RecyclerView.Vi
{
((FeatureViewHolder) holder).bind(position);
}
else if (holder instanceof FooterViewHolder)
{
((FooterViewHolder) holder).bind(mFragment.getPendingNoteText());
}
}
@Override
@@ -105,11 +119,36 @@ public class FeatureCategoryAdapter extends RecyclerView.Adapter<RecyclerView.Vi
protected static class FooterViewHolder extends RecyclerView.ViewHolder
{
FooterViewHolder(@NonNull View itemView)
private final TextInputEditText mNoteEditText;
private final View mSendNoteButton;
FooterViewHolder(@NonNull View itemView, @NonNull FooterListener listener)
{
super(itemView);
MaterialTextView categoryUnsuitableText = itemView.findViewById(R.id.editor_category_unsuitable_text);
categoryUnsuitableText.setMovementMethod(LinkMovementMethod.getInstance());
mNoteEditText = itemView.findViewById(R.id.note_edit_text);
mSendNoteButton = itemView.findViewById(R.id.send_note_button);
mSendNoteButton.setOnClickListener(v -> listener.onSendNoteClicked());
mNoteEditText.addTextChangedListener(new StringUtils.SimpleTextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
final String str = s.toString();
listener.onNoteTextChanged(str);
mSendNoteButton.setEnabled(!str.trim().isEmpty());
}
});
}
public void bind(String pendingNoteText)
{
if (!mNoteEditText.getText().toString().equals(pendingNoteText))
{
mNoteEditText.setText(pendingNoteText);
if (pendingNoteText != null)
mNoteEditText.setSelection(pendingNoteText.length());
}
mSendNoteButton.setEnabled(pendingNoteText != null && !pendingNoteText.trim().isEmpty());
}
}

View File

@@ -1,7 +1,6 @@
package app.organicmaps.editor;
import static app.organicmaps.sdk.util.Utils.getLocalizedFeatureType;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -9,21 +8,32 @@ import android.view.ViewGroup;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.organicmaps.R;
import app.organicmaps.base.BaseMwmRecyclerFragment;
import app.organicmaps.MwmApplication;
import app.organicmaps.sdk.editor.Editor;
import app.organicmaps.sdk.editor.data.FeatureCategory;
import app.organicmaps.sdk.util.Language;
import app.organicmaps.sdk.editor.OsmOAuth;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import android.text.TextUtils;
import android.content.Intent;
import android.widget.Toast;
import app.organicmaps.sdk.Framework;
import app.organicmaps.R;
import app.organicmaps.base.BaseMwmRecyclerFragment;
import app.organicmaps.dialog.EditTextDialogFragment;
import app.organicmaps.util.Utils;
import app.organicmaps.widget.SearchToolbarController;
import app.organicmaps.widget.ToolbarController;
import java.util.Arrays;
import java.util.Comparator;
public class FeatureCategoryFragment extends BaseMwmRecyclerFragment<FeatureCategoryAdapter>
public class FeatureCategoryFragment extends BaseMwmRecyclerFragment<FeatureCategoryAdapter> implements FeatureCategoryAdapter.FooterListener
{
private FeatureCategory mSelectedCategory;
protected ToolbarController mToolbarController;
private static final String NOTE_CONFIRMATION_SHOWN = "NoteConfirmationAlertWasShown";
private static String mPendingNoteText = "";
public interface FeatureCategoryListener
{
@@ -48,7 +58,8 @@ public class FeatureCategoryFragment extends BaseMwmRecyclerFragment<FeatureCate
mSelectedCategory =
Utils.getParcelable(args, FeatureCategoryActivity.EXTRA_FEATURE_CATEGORY, FeatureCategory.class);
}
mToolbarController = new SearchToolbarController(view, requireActivity()) {
mToolbarController = new SearchToolbarController(view, requireActivity())
{
@Override
protected void onTextChanged(String query)
{
@@ -104,4 +115,62 @@ public class FeatureCategoryFragment extends BaseMwmRecyclerFragment<FeatureCate
else if (getParentFragment() instanceof FeatureCategoryListener)
((FeatureCategoryListener) getParentFragment()).onFeatureCategorySelected(category);
}
public String getPendingNoteText()
{
return mPendingNoteText;
}
@Override
public void onNoteTextChanged(String newText)
{
mPendingNoteText = newText;
}
@Override
public void onSendNoteClicked()
{
if (!OsmOAuth.isAuthorized())
{
final Intent intent = new Intent(requireActivity(), OsmLoginActivity.class);
startActivity(intent);
return;
}
final double[] center = Framework.nativeGetScreenRectCenter();
final double lat = center[0];
final double lon = center[1];
if (!MwmApplication.prefs(requireContext().getApplicationContext()).contains(NOTE_CONFIRMATION_SHOWN))
{
showNoteConfirmationDialog(lat, lon, mPendingNoteText);
}
else
{
Editor.nativeCreateStandaloneNote(lat, lon, mPendingNoteText);
mPendingNoteText = "";
Toast.makeText(requireContext(), R.string.osm_note_toast, Toast.LENGTH_SHORT).show();
requireActivity().finish();
}
}
// Duplicate of showNoobDialog()
private void showNoteConfirmationDialog(double lat, double lon, String noteText)
{
new MaterialAlertDialogBuilder(requireActivity(), R.style.MwmTheme_AlertDialog)
.setTitle(R.string.editor_share_to_all_dialog_title)
.setMessage(getString(R.string.editor_share_to_all_dialog_message_1)
+ " " + getString(R.string.editor_share_to_all_dialog_message_2))
.setPositiveButton(android.R.string.ok, (dlg, which) -> {
MwmApplication.prefs(requireContext().getApplicationContext()).edit()
.putBoolean(NOTE_CONFIRMATION_SHOWN, true)
.apply();
Editor.nativeCreateStandaloneNote(lat, lon, noteText);
mPendingNoteText = "";
Toast.makeText(requireContext(), R.string.osm_note_toast, Toast.LENGTH_SHORT).show();
requireActivity().finish();
})
.setNegativeButton(R.string.cancel, null)
.show();
}
}

View File

@@ -1,8 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_gravity="center"
android:orientation="vertical"
android:background="?windowBackgroundForced"
@@ -17,10 +18,40 @@
android:textStyle="bold" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/editor_category_unsuitable_text"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_half"
android:maxWidth="500dp"
android:text="@string/editor_category_unsuitable_text"
android:textAppearance="@style/MwmTextAppearance.Body3" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/note_input_layout"
style="@style/MwmWidget.Editor.CustomTextInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColorHint="?android:textColorSecondary"
android:layout_marginTop="@dimen/margin_base_plus"
app:hintEnabled="false">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/note_edit_text"
style="@style/MwmWidget.Editor.FieldLayout.EditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/osm_note_hint"
android:inputType="textMultiLine"
android:gravity="top"
android:minLines="3"
android:maxLines="6" />
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/send_note_button"
style="@style/MwmWidget.Button.Accent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/margin_base"
android:enabled="false"
android:text="@string/editor_report_problem_send_button" />
</LinearLayout>

View File

@@ -610,9 +610,8 @@
<string name="error_enter_correct_fediverse_page">Enter a valid Mastodon username or web address</string>
<string name="error_enter_correct_bluesky_page">Enter a valid Bluesky username or web address</string>
<string name="placepage_add_place_button">Add Place to OpenStreetMap</string>
<string name="placepage_add_note">Leave Note to OpenStreetMap</string>
<string name="osm_note_hint">Describe the issue, add details, or suggest an improvement.</string>
<string name="error_enter_note">Please enter a note text.</string>
<string name="osm_note_hint">Or, alternatively, leave a note to OpenStreetMap community so that someone else can add or fix a place here.</string>
<string name="osm_note_toast">Note will be sent to OpenStreetMap</string>
<!-- Displayed when saving some edits to the map to warn against publishing personal data -->
<string name="editor_share_to_all_dialog_title">Do you want to send it to all users?</string>
<!-- Dialog before publishing the modifications to the public map. -->

View File

@@ -391,8 +391,9 @@ Java_app_organicmaps_sdk_editor_Editor_nativeCreateNote(JNIEnv * env, jclass cla
g_editableMapObject, osm::Editor::NoteProblemType::General, jni::ToNativeString(env, text));
}
JNIEXPORT void JNICALL
Java_app_organicmaps_editor_Editor_nativeCreateStandaloneNote(JNIEnv * env, jclass clazz, jdouble lat, jdouble lon,jstring text)
JNIEXPORT void JNICALL Java_app_organicmaps_sdk_editor_Editor_nativeCreateStandaloneNote(JNIEnv * env, jclass clazz,
jdouble lat, jdouble lon,
jstring text)
{
osm::Editor::Instance().CreateStandaloneNote(ms::LatLon(lat, lon), jni::ToNativeString(env, text));
}

View File

@@ -1186,7 +1186,7 @@ void Editor::CreateNote(ms::LatLon const & latLon, FeatureID const & fid,
void Editor::CreateStandaloneNote(ms::LatLon const & latLon, std::string const & noteText)
{
CHECK_THREAD_CHECKER(MainThreadChecker,(""));
CHECK_THREAD_CHECKER(MainThreadChecker, (""));
m_notes->CreateNote(latLon, noteText + "\n");
}