Skip to content

Commit b7090d9

Browse files
authored
Added voice input for caption and description (#5415)
* Fixed Grey empty screen at Upload wizard caption step after denying files permission * Empty commit * Fixed loop issue * Created docs for earlier commits * Fixed javadoc * Fixed spaces * Added added basic features to OSM Maps * Added search location feature * Added filter to Open Street Maps * Fixed chipGroup in Open Street Maps * Removed mapBox code * Removed mapBox's code * Reformat code * Reformatted code * Removed rotation feature to map * Removed rotation files and Fixed Marker click problem * Ignored failing tests * Added voice input feature * Fixed test cases * Changed caption and description text
1 parent e5c789e commit b7090d9

File tree

9 files changed

+297
-109
lines changed

9 files changed

+297
-109
lines changed

app/src/main/java/fr/free/nrw/commons/description/DescriptionEditActivity.kt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package fr.free.nrw.commons.description
22

3+
import android.app.Activity.RESULT_OK
34
import android.app.ProgressDialog
45
import android.content.Intent
56
import android.os.Bundle
67
import android.os.Parcelable
8+
import android.speech.RecognizerIntent
79
import android.view.View
810
import androidx.recyclerview.widget.LinearLayoutManager
911
import androidx.recyclerview.widget.RecyclerView
@@ -18,6 +20,7 @@ import fr.free.nrw.commons.theme.BaseActivity
1820
import fr.free.nrw.commons.upload.UploadMediaDetail
1921
import fr.free.nrw.commons.upload.UploadMediaDetailAdapter
2022
import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog
23+
import timber.log.Timber
2124
import javax.inject.Inject
2225

2326
/**
@@ -55,6 +58,8 @@ class DescriptionEditActivity : BaseActivity(), UploadMediaDetailAdapter.EventLi
5558

5659
private lateinit var binding: ActivityDescriptionEditBinding
5760

61+
private val REQUEST_CODE_FOR_VOICE_INPUT = 1213
62+
5863
override fun onCreate(savedInstanceState: Bundle?) {
5964
super.onCreate(savedInstanceState)
6065

@@ -78,7 +83,7 @@ class DescriptionEditActivity : BaseActivity(), UploadMediaDetailAdapter.EventLi
7883
* @param descriptionAndCaptions list of description and caption
7984
*/
8085
private fun initRecyclerView(descriptionAndCaptions: ArrayList<UploadMediaDetail>?) {
81-
uploadMediaDetailAdapter = UploadMediaDetailAdapter(
86+
uploadMediaDetailAdapter = UploadMediaDetailAdapter(this,
8287
savedLanguageValue, descriptionAndCaptions, recentLanguagesDao)
8388
uploadMediaDetailAdapter.setCallback { titleStringID: Int, messageStringId: Int ->
8489
showInfoAlert(
@@ -175,4 +180,15 @@ class DescriptionEditActivity : BaseActivity(), UploadMediaDetailAdapter.EventLi
175180
progressDialog!!.setCanceledOnTouchOutside(false)
176181
progressDialog!!.show()
177182
}
183+
184+
override
185+
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
186+
super.onActivityResult(requestCode, resultCode, data);
187+
if (requestCode == REQUEST_CODE_FOR_VOICE_INPUT) {
188+
if (resultCode == RESULT_OK && data != null) {
189+
val result = data.getStringArrayListExtra( RecognizerIntent.EXTRA_RESULTS )
190+
uploadMediaDetailAdapter.handleSpeechResult(result!![0]) }
191+
else { Timber.e("Error %s", resultCode) }
192+
}
193+
}
178194
}

app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetailAdapter.java

Lines changed: 125 additions & 44 deletions
Large diffs are not rendered by default.

app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,13 @@
66
import static fr.free.nrw.commons.utils.ImageUtils.getErrorMessageForResult;
77

88
import android.annotation.SuppressLint;
9-
import android.content.ContentResolver;
10-
import android.content.Context;
119
import android.content.Intent;
1210
import android.net.Uri;
1311
import android.os.Bundle;
12+
import android.speech.RecognizerIntent;
1413
import android.text.TextUtils;
1514
import android.view.LayoutInflater;
1615
import android.view.View;
17-
import android.view.View.OnClickListener;
1816
import android.view.ViewGroup;
1917
import android.widget.CheckBox;
2018
import android.widget.ImageView;
@@ -40,7 +38,6 @@
4038
import fr.free.nrw.commons.filepicker.UploadableFile;
4139
import fr.free.nrw.commons.kvstore.JsonKvStore;
4240
import fr.free.nrw.commons.location.LatLng;
43-
import fr.free.nrw.commons.location.LocationServiceManager;
4441
import fr.free.nrw.commons.nearby.Place;
4542
import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao;
4643
import fr.free.nrw.commons.settings.Prefs;
@@ -54,8 +51,8 @@
5451
import fr.free.nrw.commons.utils.DialogUtil;
5552
import fr.free.nrw.commons.utils.ImageUtils;
5653
import fr.free.nrw.commons.utils.ViewUtil;
57-
import fr.free.nrw.commons.R.drawable.*;
5854
import java.io.File;
55+
import java.util.ArrayList;
5956
import java.util.List;
6057
import java.util.Locale;
6158
import java.util.Objects;
@@ -69,6 +66,7 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
6966

7067
private static final int REQUEST_CODE = 1211;
7168
private static final int REQUEST_CODE_FOR_EDIT_ACTIVITY = 1212;
69+
private static final int REQUEST_CODE_FOR_VOICE_INPUT = 1213;
7270

7371
/**
7472
* A key for applicationKvStore. By this key we can retrieve the location of last UploadItem ex.
@@ -238,7 +236,7 @@ private void initPresenter() {
238236
* init the description recycler veiw and caption recyclerview
239237
*/
240238
private void initRecyclerView() {
241-
uploadMediaDetailAdapter = new UploadMediaDetailAdapter(
239+
uploadMediaDetailAdapter = new UploadMediaDetailAdapter(this,
242240
defaultKvStore.getString(Prefs.DESCRIPTION_LANGUAGE, ""), recentLanguagesDao);
243241
uploadMediaDetailAdapter.setCallback(this::showInfoAlert);
244242
uploadMediaDetailAdapter.setEventListener(this);
@@ -558,7 +556,6 @@ private void goToLocationPickerActivity(final UploadItem uploadItem) {
558556
public void onActivityResult(final int requestCode, final int resultCode,
559557
@Nullable final Intent data) {
560558
super.onActivityResult(requestCode, resultCode, data);
561-
562559
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
563560

564561
assert data != null;
@@ -597,6 +594,15 @@ public void onActivityResult(final int requestCode, final int resultCode,
597594
Timber.e(e);
598595
}
599596
}
597+
else if (requestCode == REQUEST_CODE_FOR_VOICE_INPUT) {
598+
if (resultCode == RESULT_OK && data != null) {
599+
ArrayList<String> result = data.getStringArrayListExtra(
600+
RecognizerIntent.EXTRA_RESULTS);
601+
uploadMediaDetailAdapter.handleSpeechResult(result.get(0));
602+
}else {
603+
Timber.e("Error %s", resultCode);
604+
}
605+
}
600606
}
601607

602608
/**
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="21dp"
3+
android:height="21dp"
4+
android:viewportHeight="24.0"
5+
android:viewportWidth="24.0">
6+
<path
7+
android:fillColor="#FF1E8CAB"
8+
android:pathData="M12,15c1.66,0 2.99,-1.34 2.99,-3L15,6c0,-1.66 -1.34,-3 -3,-3S9,4.34 9,6v6c0,1.66 1.34,3 3,3zM17.3,12c0,3 -2.54,5.1 -5.3,5.1S6.7,15 6.7,12L5,12c0,3.42 2.72,6.23 6,6.72L11,22h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z" />
9+
</vector>
10+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<selector xmlns:android="http://schemas.android.com/apk/res/android">
3+
<item android:state_pressed="true"
4+
android:drawable="@color/pressed_button_light" />
5+
<item android:state_focused="false"
6+
android:drawable="@android:color/transparent" />
7+
</selector>

app/src/main/res/drawable/ic_open.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="20dp"
3+
android:height="20dp"
4+
android:viewportWidth="20"
5+
android:tint="?attr/editTextColor"
6+
android:viewportHeight="20">
7+
<path
8+
android:pathData="M17.778,17.778H2.223V2.222H10V0H2.223C0.989,0 0,1 0,2.222V17.778C0,19 0.989,20 2.223,20H17.778C19,20 20,19 20,17.778V10H17.778V17.778ZM12.223,0V2.222H16.212L5.289,13.144L6.856,14.711L17.778,3.789V7.778H20V0H12.223Z"
9+
android:fillColor="#8F000000"/>
10+
</vector>

app/src/main/res/layout/row_item_description.xml

Lines changed: 106 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -7,68 +7,118 @@
77
android:layout_marginVertical="8dp"
88
app:elevation="6dp">
99

10-
<androidx.constraintlayout.widget.ConstraintLayout
10+
<androidx.constraintlayout.widget.ConstraintLayout
11+
android:layout_width="match_parent"
12+
android:layout_height="match_parent"
13+
android:layout_marginHorizontal="20dp"
14+
android:layout_marginVertical="20dp">
15+
16+
<ImageView
17+
android:id="@+id/btn_remove"
18+
android:layout_width="24dp"
19+
android:layout_height="wrap_content"
20+
android:contentDescription="@string/remove"
21+
android:visibility="visible"
22+
app:layout_constraintEnd_toEndOf="parent"
23+
app:layout_constraintTop_toTopOf="parent"
24+
app:srcCompat="@drawable/ic_remove" />
25+
26+
<TextView
27+
android:id="@+id/description_languages"
28+
android:layout_width="wrap_content"
29+
android:layout_height="wrap_content"
30+
android:clickable="true"
31+
android:drawableRight="@drawable/ic_baseline_arrow_drop_down_24"
32+
android:padding="@dimen/dimen_2"
33+
android:textSize="18sp"
34+
app:layout_constraintStart_toStartOf="parent"
35+
app:layout_constraintTop_toTopOf="parent" />
36+
37+
<com.google.android.material.textfield.TextInputLayout
38+
android:id="@+id/caption_item_edit_text_input_layout"
1139
android:layout_width="match_parent"
12-
android:layout_height="match_parent"
13-
android:layout_marginHorizontal="20dp"
14-
android:layout_marginVertical="20dp">
40+
android:layout_height="wrap_content"
41+
android:layout_marginTop="4dp"
42+
app:layout_constraintEnd_toEndOf="parent"
43+
app:layout_constraintStart_toStartOf="parent"
44+
app:layout_constraintTop_toBottomOf="@+id/description_languages">
45+
46+
<fr.free.nrw.commons.ui.PasteSensitiveTextInputEditText
47+
android:id="@+id/caption_item_edit_text"
48+
android:layout_width="match_parent"
49+
android:layout_height="match_parent"
50+
android:hint="@string/share_caption_hint"
51+
android:imeOptions="actionNext|flagNoExtractUi"
52+
android:inputType="text"
53+
app:allowFormatting="false" />
54+
</com.google.android.material.textfield.TextInputLayout>
1555

16-
<ImageView
17-
android:id="@+id/btn_remove"
18-
android:layout_width="24dp"
19-
android:layout_height="wrap_content"
20-
android:contentDescription="@string/remove"
21-
android:visibility="visible"
22-
app:layout_constraintBottom_toTopOf="@+id/caption_item_edit_text_input_layout"
23-
app:layout_constraintEnd_toEndOf="parent"
24-
app:srcCompat="@drawable/ic_remove" />
56+
<LinearLayout
57+
android:id="@+id/ll_write_better_caption"
58+
android:layout_width="wrap_content"
59+
android:layout_height="wrap_content"
60+
android:layout_marginStart="2dp"
61+
android:background="@drawable/clicked_linearlayout_background"
62+
android:clickable="true"
63+
android:gravity="center_vertical"
64+
app:layout_constraintStart_toStartOf="parent"
65+
app:layout_constraintTop_toBottomOf="@+id/caption_item_edit_text_input_layout">
2566

26-
<TextView
27-
android:id="@+id/description_languages"
28-
android:layout_width="wrap_content"
29-
android:layout_height="wrap_content"
30-
android:clickable="true"
31-
android:drawableRight="@drawable/ic_baseline_arrow_drop_down_24"
32-
android:padding="@dimen/dimen_2"
33-
android:textSize="18sp"
34-
app:layout_constraintBottom_toTopOf="@+id/caption_item_edit_text_input_layout"
35-
app:layout_constraintStart_toStartOf="parent" />
67+
<TextView
68+
android:layout_width="wrap_content"
69+
android:layout_height="wrap_content"
70+
android:text="@string/learn_how_to_write_a_useful_caption"
71+
android:textSize="12sp" />
3672

37-
<com.google.android.material.textfield.TextInputLayout
38-
android:id="@+id/caption_item_edit_text_input_layout"
39-
android:layout_width="match_parent"
40-
android:layout_height="wrap_content"
41-
app:layout_constraintBottom_toTopOf="@id/description_item_edit_text_input_layout"
42-
app:layout_constraintEnd_toEndOf="parent"
43-
app:layout_constraintStart_toStartOf="parent">
73+
<ImageView
74+
android:layout_width="10dp"
75+
android:layout_height="10dp"
76+
android:layout_marginStart="6dp"
77+
app:srcCompat="@drawable/ic_open" />
78+
79+
</LinearLayout>
80+
81+
<com.google.android.material.textfield.TextInputLayout
82+
android:id="@+id/description_item_edit_text_input_layout"
83+
android:layout_width="match_parent"
84+
android:layout_height="wrap_content"
85+
android:layout_marginTop="4dp"
86+
app:layout_constraintEnd_toEndOf="parent"
87+
app:layout_constraintStart_toStartOf="parent"
88+
app:layout_constraintTop_toBottomOf="@+id/ll_write_better_caption">
4489

45-
<fr.free.nrw.commons.ui.PasteSensitiveTextInputEditText
46-
android:id="@+id/caption_item_edit_text"
47-
android:layout_width="match_parent"
48-
android:layout_height="match_parent"
49-
android:hint="@string/share_caption_hint"
50-
android:imeOptions="actionNext|flagNoExtractUi"
51-
android:inputType="text"
52-
app:allowFormatting="false" />
53-
</com.google.android.material.textfield.TextInputLayout>
90+
<fr.free.nrw.commons.ui.PasteSensitiveTextInputEditText
91+
android:id="@+id/description_item_edit_text"
92+
android:layout_width="match_parent"
93+
android:layout_height="match_parent"
94+
android:hint="@string/share_description_hint"
95+
android:imeOptions="actionNext|flagNoExtractUi"
96+
android:inputType="textMultiLine"
97+
app:allowFormatting="false" />
98+
</com.google.android.material.textfield.TextInputLayout>
5499

55-
<com.google.android.material.textfield.TextInputLayout
56-
android:id="@+id/description_item_edit_text_input_layout"
57-
android:layout_width="match_parent"
58-
android:layout_height="wrap_content"
59-
app:layout_constraintBottom_toBottomOf="parent"
60-
app:layout_constraintEnd_toEndOf="parent"
61-
app:layout_constraintStart_toStartOf="parent">
100+
<LinearLayout
101+
android:id="@+id/ll_write_better_description"
102+
android:layout_width="wrap_content"
103+
android:layout_height="wrap_content"
104+
android:layout_marginStart="2dp"
105+
android:background="@drawable/clicked_linearlayout_background"
106+
android:clickable="true"
107+
android:gravity="center_vertical"
108+
app:layout_constraintStart_toStartOf="parent"
109+
app:layout_constraintTop_toBottomOf="@+id/description_item_edit_text_input_layout">
62110

63-
<fr.free.nrw.commons.ui.PasteSensitiveTextInputEditText
64-
android:id="@+id/description_item_edit_text"
65-
android:layout_width="match_parent"
66-
android:layout_height="match_parent"
67-
android:hint="@string/share_description_hint"
68-
android:imeOptions="actionNext|flagNoExtractUi"
69-
android:inputType="textMultiLine"
70-
app:allowFormatting="false" />
71-
</com.google.android.material.textfield.TextInputLayout>
111+
<TextView
112+
android:layout_width="wrap_content"
113+
android:layout_height="wrap_content"
114+
android:text="@string/learn_how_to_write_a_useful_description"
115+
android:textSize="12sp" />
72116

73-
</androidx.constraintlayout.widget.ConstraintLayout>
117+
<ImageView
118+
android:layout_width="10dp"
119+
android:layout_height="10dp"
120+
android:layout_marginStart="6dp"
121+
app:srcCompat="@drawable/ic_open" />
122+
</LinearLayout>
123+
</androidx.constraintlayout.widget.ConstraintLayout>
74124
</androidx.cardview.widget.CardView>

app/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,8 @@ Upload your first media by tapping on the add button.</string>
784784
<string name="storage_permissions_denied">Storage Permissions Denied</string>
785785
<string name="unable_to_share_upload_item">Unable to share this item</string>
786786
<string name="permissions_are_required_for_functionality">Permissions are required for functionality</string>
787+
<string name="learn_how_to_write_a_useful_description">Learn how to write a useful description</string>
788+
<string name="learn_how_to_write_a_useful_caption">Learn how to write a useful caption</string>
787789
<plurals name="custom_picker_images_selected_title_appendix">
788790
<item quantity="one">%d image selected</item>
789791
<item quantity="other">%d images selected</item>

app/src/test/kotlin/fr/free/nrw/commons/upload/UploadMediaDetailAdapterUnitTest.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@ import fr.free.nrw.commons.recentlanguages.Language
1717
import fr.free.nrw.commons.recentlanguages.RecentLanguagesAdapter
1818
import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao
1919
import fr.free.nrw.commons.settings.SettingsFragment
20+
import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment
2021
import org.junit.Assert
2122
import org.junit.Before
2223
import org.junit.Test
2324
import org.junit.runner.RunWith
2425
import org.mockito.Mock
2526
import org.mockito.Mockito
27+
import org.mockito.Mockito.mock
2628
import org.mockito.MockitoAnnotations
2729
import org.powermock.reflect.Whitebox
2830
import org.robolectric.Robolectric
@@ -55,6 +57,9 @@ class UploadMediaDetailAdapterUnitTest {
5557
@Mock
5658
private lateinit var textView: TextView
5759

60+
@Mock
61+
private lateinit var fragment : UploadMediaDetailFragment
62+
5863
@Mock
5964
private lateinit var view: View
6065

@@ -69,7 +74,8 @@ class UploadMediaDetailAdapterUnitTest {
6974
MockitoAnnotations.openMocks(this)
7075
uploadMediaDetails = mutableListOf(uploadMediaDetail, uploadMediaDetail)
7176
activity = Robolectric.buildActivity(UploadActivity::class.java).get()
72-
adapter = UploadMediaDetailAdapter("", recentLanguagesDao)
77+
fragment = mock(UploadMediaDetailFragment::class.java)
78+
adapter = UploadMediaDetailAdapter(fragment,"", recentLanguagesDao)
7379
context = ApplicationProvider.getApplicationContext()
7480
Whitebox.setInternalState(adapter, "uploadMediaDetails", uploadMediaDetails)
7581
Whitebox.setInternalState(adapter, "eventListener", eventListener)

0 commit comments

Comments
 (0)