Author: Adam Evyčędo <git@apiote.xyz>
select basic options for journeys
%!v(PANIC=String method: strings: negative Repeat count)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8b27f32a4ad2ff9e1d791d1c289cbb213be32dc8..41f9609d54c86b09821831f26252f2a83173b254 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,4 +1,3 @@ - import com.android.build.gradle.internal.tasks.factory.dependsOn import com.android.build.gradle.tasks.ExtractDeepLinksTask import com.android.build.gradle.tasks.MergeResources @@ -32,7 +31,7 @@ versionCode = 34 versionName = "3.8.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - resourceConfigurations += listOf("en", "de", "en-rGB", "en-rUS", "et", "fr", "it", "pl") + resourceConfigurations += listOf("en", "de", "en-rGB", "en-rUS", "et", "fr", "it", "pl") // TODO ru, ta } buildTypes { @@ -125,6 +124,8 @@ implementation("ch.acra:acra-notification:5.12.0") implementation("com.squareup.okhttp3:okhttp:4.12.0") implementation("com.squareup.moshi:moshi-kotlin:1.15.2") implementation("androidx.activity:activity:1.9.3") + implementation("com.google.maps.android:maps-ktx:5.1.1") + implementation("com.google.maps.android:maps-utils-ktx:5.1.1") coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4") diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/api/transitousJourney.kt b/app/src/main/java/xyz/apiote/bimba/czwek/api/transitousJourney.kt index bd47fc72e1ba1bcaa102c8766b5930c268ad4b87..9a2eec19639badea4389b75735e648bbde2c93d1 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/api/transitousJourney.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/api/transitousJourney.kt @@ -96,8 +96,8 @@ it.agencyName, it.distance?.toDouble()?.let { Metre(it) }, Second(it.duration), (it.intermediateStops ?: emptyList()).map { Place(it) }, - /*it.legGeometry, - it.rental, + it.legGeometry, + /*it.rental, it.steps,*/ ) } diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/DashboardViewModel.kt b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/DashboardViewModel.kt index 6acbe27969f244d5917f458f5f24867189b522df..877dbb6656342cf073a915c8c42fe80389e580b7 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/DashboardViewModel.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/DashboardViewModel.kt @@ -10,7 +10,10 @@ import androidx.lifecycle.ViewModel import com.google.android.material.chip.Chip import com.google.android.material.textfield.TextInputEditText import xyz.apiote.bimba.czwek.repo.Place +import xyz.apiote.bimba.czwek.repo.TimeReference import xyz.apiote.bimba.czwek.search.Query +import java.time.LocalDate +import java.time.LocalTime class DashboardViewModel : ViewModel() { companion object { @@ -57,5 +60,9 @@ DEST_KEY to null ) val textInputs = mutableMapOf<String, TextInputEditText>() + + var timeReference: TimeReference = TimeReference.DEPART_AFTER + var date: Long? = null + var time: LocalTime? = null } diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/journey/JourneyFragment.kt b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/journey/JourneyFragment.kt index 3b7fe0587642546d30be3ebd49b23b9aec4bc173..ad046266f26f5a5fbe5ca3d90376e211172aa46f 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/journey/JourneyFragment.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/ui/journey/JourneyFragment.kt @@ -16,13 +16,16 @@ import android.os.Bundle import android.text.Editable import android.text.Spanned import android.text.TextWatcher +import android.text.format.DateUtils import android.text.style.ImageSpan import android.util.Log import android.view.KeyEvent import android.view.LayoutInflater +import android.view.Menu import android.view.MotionEvent.ACTION_UP import android.view.View import android.view.ViewGroup +import android.widget.PopupMenu import androidx.activity.result.contract.ActivityResultContracts import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat @@ -34,6 +37,9 @@ import androidx.swiperefreshlayout.widget.CircularProgressDrawable import com.google.android.material.chip.Chip import com.google.android.material.chip.ChipDrawable import com.google.android.material.chip.ChipGroup +import com.google.android.material.datepicker.MaterialDatePicker +import com.google.android.material.timepicker.MaterialTimePicker +import com.google.android.material.timepicker.TimeFormat import com.google.openlocationcode.OpenLocationCode import xyz.apiote.bimba.czwek.R import xyz.apiote.bimba.czwek.dashboard.DashboardViewModel @@ -41,11 +47,17 @@ import xyz.apiote.bimba.czwek.dashboard.MainActivity import xyz.apiote.bimba.czwek.databinding.FragmentJourneyBinding import xyz.apiote.bimba.czwek.dpToPixelI import xyz.apiote.bimba.czwek.journeys.JourneysActivity +import xyz.apiote.bimba.czwek.repo.JourneyParams import xyz.apiote.bimba.czwek.repo.Place import xyz.apiote.bimba.czwek.repo.Position +import xyz.apiote.bimba.czwek.repo.TimeReference import xyz.apiote.bimba.czwek.search.Query import xyz.apiote.bimba.czwek.search.Query.Mode import xyz.apiote.bimba.czwek.search.ResultsActivity +import java.time.Instant +import java.time.LocalDate +import java.time.LocalTime +import java.time.ZoneId class JourneyFragment : Fragment(), LocationListener { companion object { @@ -152,6 +164,11 @@ val intent = JourneysActivity.getIntent( requireContext(), dashboard.viewModel.data[DashboardViewModel.ORIGIN_KEY]!!.value!!, dashboard.viewModel.data[DashboardViewModel.DEST_KEY]!!.value!!, + JourneyParams( + dashboard.viewModel.timeReference, + dashboard.viewModel.date?.let { epochMilli -> Instant.ofEpochMilli(epochMilli) }?.atZone(ZoneId.systemDefault())?.toLocalDate(), + dashboard.viewModel.time + ) ) startActivity(intent) } @@ -214,7 +231,118 @@ afterTextChanged(DashboardViewModel.DEST_KEY, binding.destinationSuggestions, s, inflater) } }) + setTimeReference(dashboard.viewModel.timeReference.id()) + binding.chipTimeReference.setOnClickListener { + val menu = PopupMenu(requireContext(), it) + menu.menu.add( + Menu.NONE, + TimeReference.DEPART_AFTER.id(), + TimeReference.DEPART_AFTER.ordinal, + R.string.depart_after + ) + menu.menu.add( + Menu.NONE, + TimeReference.ARRIVE_BY.id(), + TimeReference.ARRIVE_BY.ordinal, + R.string.arrive_by + ) + menu.setOnMenuItemClickListener { + setTimeReference(it.itemId) + true + } + menu.show() + } + + if (dashboard.viewModel.date != null) { + binding.chipDate.text = + DateUtils.formatDateTime( + context, + dashboard.viewModel.date!!, + DateUtils.FORMAT_SHOW_DATE + ) + } else { + binding.chipDate.setText(R.string.today) + } + binding.chipDate.setOnClickListener { _ -> + MaterialDatePicker.Builder.datePicker().setTitleText(R.string.title_select_date_journey) + .setNegativeButtonText(R.string.clear_date_selection) + .apply { + if (dashboard.viewModel.date != null) { + setSelection(dashboard.viewModel.date!!) + } + } + .build() + .apply { + addOnNegativeButtonClickListener { _ -> + dashboard.viewModel.date = null + binding.chipDate.setText(R.string.today) + } + addOnPositiveButtonClickListener { t -> + dashboard.viewModel.date = t + binding.chipDate.text = + DateUtils.formatDateTime(context, t, DateUtils.FORMAT_SHOW_DATE) + } + }.show((activity as MainActivity).supportFragmentManager, null) + } + + + if (dashboard.viewModel.date != null) { + binding.chipTime.text = + DateUtils.formatDateTime( + context, + dashboard.viewModel.time!!.atDate(LocalDate.now()).atZone(ZoneId.systemDefault()) + .toEpochSecond() * 1000, + DateUtils.FORMAT_SHOW_TIME + ) + } else { + binding.chipTime.setText(R.string.now) + } + binding.chipTime.setOnClickListener { _ -> + val picker = MaterialTimePicker.Builder().setTitleText(R.string.title_select_time_journey) + .setTimeFormat(TimeFormat.CLOCK_24H) + .setNegativeButtonText(R.string.clear_date_selection) + .apply { + if (dashboard.viewModel.time != null) { + setHour(dashboard.viewModel.time!!.hour) + setMinute(dashboard.viewModel.time!!.minute) + } else { + setHour(LocalTime.now().hour) + setMinute(LocalTime.now().minute) + } + }.build() + + picker.apply { + addOnNegativeButtonClickListener { _ -> + dashboard.viewModel.time = null + binding.chipTime.setText(R.string.now) + } + addOnPositiveButtonClickListener { _ -> + dashboard.viewModel.time = LocalTime.of(picker.hour, picker.minute) + binding.chipTime.text = + DateUtils.formatDateTime( + context, + dashboard.viewModel.time!!.atDate(LocalDate.now()).atZone(ZoneId.systemDefault()) + .toEpochSecond() * 1000, + DateUtils.FORMAT_SHOW_TIME + ) + } + }.show((activity as MainActivity).supportFragmentManager, null) + } return root + } + + private fun setTimeReference(option: Int) { + when (option) { + TimeReference.DEPART_AFTER.id() -> { + binding.chipTimeReference.setText(R.string.depart_after) + dashboard.viewModel.timeReference = TimeReference.DEPART_AFTER + } + + TimeReference.ARRIVE_BY.id() -> { + binding.chipTimeReference.setText(R.string.arrive_by) + dashboard.viewModel.timeReference = TimeReference.ARRIVE_BY + } + } } private fun afterTextChanged( diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/journeys/JourneysActivity.kt b/app/src/main/java/xyz/apiote/bimba/czwek/journeys/JourneysActivity.kt index 5f9707b1c1f5a80b54d9c20dc1ee9d4753f9216e..89291eb7541d2ab0989fc5e0f2b18f0ed861a7a7 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/journeys/JourneysActivity.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/journeys/JourneysActivity.kt @@ -9,23 +9,32 @@ import android.content.Intent import android.content.res.Configuration.UI_MODE_NIGHT_MASK import android.content.res.Configuration.UI_MODE_NIGHT_UNDEFINED import android.content.res.Configuration.UI_MODE_NIGHT_YES +import android.graphics.DashPathEffect import android.os.Build import android.os.Bundle import android.view.View import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.content.res.AppCompatResources import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager +import com.google.maps.android.PolyUtil import org.osmdroid.tileprovider.tilesource.TileSourceFactory import org.osmdroid.util.BoundingBox +import org.osmdroid.util.GeoPoint import org.osmdroid.views.CustomZoomButtonsController +import org.osmdroid.views.overlay.Marker +import org.osmdroid.views.overlay.Polyline import org.osmdroid.views.overlay.TilesOverlay import org.osmdroid.views.overlay.gestures.RotationGestureOverlay +import xyz.apiote.bimba.czwek.R import xyz.apiote.bimba.czwek.databinding.ActivityJourneysBinding import xyz.apiote.bimba.czwek.dpToPixelI +import xyz.apiote.bimba.czwek.repo.JourneyParams +import xyz.apiote.bimba.czwek.repo.LineType import xyz.apiote.bimba.czwek.repo.Place import kotlin.math.max import kotlin.math.min @@ -36,14 +45,17 @@ companion object { const val ORIGIN_PARAM = "origin" const val DESTINATION_PARAM = "destination" + const val PARAMS_PARAM = "params" fun getIntent( context: Context, origin: Place, destination: Place, + params: JourneyParams ) = Intent(context, JourneysActivity::class.java).apply { putExtra(ORIGIN_PARAM, origin) putExtra(DESTINATION_PARAM, destination) + putExtra(PARAMS_PARAM, params) } } @@ -79,7 +91,7 @@ } binding.map.setTileSource(TileSourceFactory.MAPNIK) if (((resources?.configuration?.uiMode ?: UI_MODE_NIGHT_UNDEFINED) - and UI_MODE_NIGHT_MASK) == UI_MODE_NIGHT_YES + and UI_MODE_NIGHT_MASK) == UI_MODE_NIGHT_YES ) { binding.map.overlayManager.tilesOverlay.setColorFilter(TilesOverlay.INVERT_COLORS) } @@ -103,8 +115,45 @@ binding.emptyImage.visibility = View.VISIBLE } else { binding.journeys.visibility = View.VISIBLE binding.journeys.adapter = JourneysAdapter(layoutInflater, this, it) { + binding.map.overlays.removeAll { true } + + val originMarker = Marker(binding.map).apply { + position = GeoPoint(it.legs[0].origin.latitude, it.legs[0].origin.longitude) + setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM) + val scale = binding.map.zoomLevelDouble / -4 + 5.5 + icon = AppCompatResources.getDrawable( + this@JourneysActivity, + R.drawable.pin // R.drawable.legOrigin + ) // TODO scale and colour + } + binding.map.overlays.add(originMarker) + + val destinationMarker = Marker(binding.map).apply { + position = GeoPoint(it.legs[0].origin.latitude, it.legs[0].origin.longitude) + setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM) + val scale = binding.map.zoomLevelDouble / -4 + 5.5 + icon = AppCompatResources.getDrawable( + this@JourneysActivity, + R.drawable.pin //R.drawable.legDestination + ) // TODO scale and colour + } + binding.map.overlays.add(destinationMarker) + + it.legs.forEachIndexed { i, leg -> + val shapePoints = PolyUtil.decode(leg.shape.points).map { + GeoPoint(it.latitude, it.longitude) + } + val shape = Polyline() + val paint = shape.outlinePaint + paint.color = leg.start.vehicle.Line.colour.toInt() // TODO contrast + if (leg.start.vehicle.Line.kind == LineType.WALK) { // TODO and other active + paint.setPathEffect(DashPathEffect(floatArrayOf(10f, 10f), 0f)) + } + shape.setPoints(shapePoints) + binding.map.overlays.add(shape) + } + binding.map.invalidate() // todo move map accordingly - // todo show shape on map } } } @@ -119,7 +168,7 @@ fun zoomMap(margin: Int = 0, box: BoundingBox? = null) { val origin = getOrigin() val destination = getDestination() - val bb = box?:BoundingBox( + val bb = box ?: BoundingBox( max(origin.latitude, destination.latitude), max(origin.longitude, destination.longitude), min(origin.latitude, destination.latitude), diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Journey.kt b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Journey.kt index fe259f7d7cbf3b9822d9bc4dbb3f5575e48f9d13..0446e96f18431cc2725c62469f36fe8ab25fc40f 100644 --- a/app/src/main/java/xyz/apiote/bimba/czwek/repo/Journey.kt +++ b/app/src/main/java/xyz/apiote/bimba/czwek/repo/Journey.kt @@ -6,6 +6,7 @@ package xyz.apiote.bimba.czwek.repo import android.os.Parcelable import kotlinx.parcelize.Parcelize +import xyz.apiote.bimba.czwek.api.transitous.model.EncodedPolyline import xyz.apiote.bimba.czwek.api.transitous.model.Place import xyz.apiote.bimba.czwek.units.DistanceUnit import xyz.apiote.bimba.czwek.units.TimeUnit @@ -22,8 +23,8 @@ val agencyName: String?, val distance: DistanceUnit?, val duration: TimeUnit, val intermediateStops: List<xyz.apiote.bimba.czwek.repo.Place>, + val shape: EncodedPolyline, // TODO to list of points /* TODO - val shape: EncodedPolyline, val rental: Rental?, val steps: List<StepInstruction>?,*/ ) diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/repo/JourneyParams.kt b/app/src/main/java/xyz/apiote/bimba/czwek/repo/JourneyParams.kt new file mode 100644 index 0000000000000000000000000000000000000000..bf6d60f1e2d1573b10dce5d35e986ca16abd62ec --- /dev/null +++ b/app/src/main/java/xyz/apiote/bimba/czwek/repo/JourneyParams.kt @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: Adam Evyčędo +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package xyz.apiote.bimba.czwek.repo + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize +import java.time.LocalDate +import java.time.LocalTime + + +enum class TimeReference { + DEPART_AFTER, ARRIVE_BY; + + fun id(): Int { + return when (this) { + DEPART_AFTER -> 1 + ARRIVE_BY -> 2 + } + } +} + +@Parcelize +data class JourneyParams(val timeReference: TimeReference, val date: LocalDate?, val time: LocalTime?) : Parcelable +/* + date ?: Instant.now().atZone(ZoneId.systemDefault()).toLocalDate() + time ?: Instant.now().atZone(ZoneId.systemDefault()).toLocalTime() +*/ \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_journey.xml b/app/src/main/res/layout/fragment_journey.xml index 649a53bbc3018e4aaf0ede4e62a3e1d7161495d6..568d127756bb13775605356cb572a6d9f4cf5484 100644 --- a/app/src/main/res/layout/fragment_journey.xml +++ b/app/src/main/res/layout/fragment_journey.xml @@ -142,12 +142,12 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/materialDivider"> - <!-- on click menu: https://stackoverflow.com/a/67764469 --> <com.google.android.material.chip.Chip android:id="@+id/chip_time_reference" style="@style/Widget.Material3.Chip.Filter" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:checkable="false" android:text="@string/depart_after" app:checkedIconEnabled="false" app:closeIcon="@drawable/dropdown" @@ -159,6 +159,7 @@ android:id="@+id/chip_date" style="@style/Widget.Material3.Chip.Filter" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:checkable="false" android:text="@string/today" app:checkedIconEnabled="false" app:closeIcon="@drawable/dropdown" @@ -170,6 +171,7 @@ android:id="@+id/chip_time" style="@style/Widget.Material3.Chip.Filter" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:checkable="false" android:text="@string/now" app:checkedIconEnabled="false" app:closeIcon="@drawable/dropdown" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8bbbfa15f1a140cd61743fc3b96b8dbabc6498c5..2530fb270cad59d4a51f216f271bcb98cc88904a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -313,4 +313,6 @@use as origin <string name="use_as_destination">use as destination</string> <string name="here">here</string> <string name="no_journeys_found">No journeys found</string> + <string name="title_select_date_journey">Select date of the journey</string> + <string name="title_select_time_journey">Select journey time</string> </resources> diff --git a/app/src/main/res/values-en-rGB/strings.xml b/app/src/main/res/values-en-rGB/strings.xml index 722359f1e44be468bf3f3c1b85e5c959de1db869..8d60ef8d598857721c521b96aa430bf03311e0aa 100644 --- a/app/src/main/res/values-en-rGB/strings.xml +++ b/app/src/main/res/values-en-rGB/strings.xml @@ -292,4 +292,6 @@approx. dep. <string name="departure">departure</string> <string name="approximately">approximately</string> <string name="depart_after">Depart after</string> + <string name="title_select_date_journey">Select date of the journey</string> + <string name="title_select_time_journey">Select journey time</string> </resources> diff --git a/app/src/main/res/values-en-rUS/strings.xml b/app/src/main/res/values-en-rUS/strings.xml index 8c8cbd4b80127310143ac5cf40cb52c6839db3cd..682a4d16fca887b28b763a9c5638269edfcd1a53 100644 --- a/app/src/main/res/values-en-rUS/strings.xml +++ b/app/src/main/res/values-en-rUS/strings.xml @@ -290,4 +290,6 @@approx. dep. <string name="departure">departure</string> <string name="approximately">approximately</string> <string name="depart_after">Depart after</string> + <string name="title_select_date_journey">Select date of the journey</string> + <string name="title_select_time_journey">Select journey time</string> </resources> \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 6963c8fc70430231237b20797a4b11eb06a2e993..059707a3db97ccd1a5cca90ae4651649260decd4 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -1,4 +1,9 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- +SPDX-FileCopyrightText: Adam Evyčędo and contributors using Weblate + +SPDX-License-Identifier: GPL-3.0-or-later +--> <resources> <string name="error_offline">Вы находитесь вне сети. Подключитесь к Интернету</string> <string name="error_429">Превышен лимит запросов. Попробуйте снова позже.</string> diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index 689b6c6b21d2fe903111edb5134e62f730e974a3..dbe162f809d28e80e67b8ef1d8e0027e7edf1c6e 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -1,4 +1,9 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- +SPDX-FileCopyrightText: Adam Evyčędo and contributors using Weblate + +SPDX-License-Identifier: GPL-3.0-or-later +--> <resources> <plurals name="distance_in_yd_cd"> <item quantity="one">%1$d முற்றத்தில்</item> diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index a6b3daec9354f9ae75cdf8d94a67446c6227dd96..0009fe182a89b8cd7a68418e441239b391e8370d 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -1,2 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- +SPDX-FileCopyrightText: Adam Evyčędo and contributors using Weblate + +SPDX-License-Identifier: GPL-3.0-or-later +--> <resources></resources> \ No newline at end of file diff --git a/app/src/main/res/xml/locales_config.xml b/app/src/main/res/xml/locales_config.xml index c5a157e555021e64b2eb8268a1a74397eb19311e..a955042c052f815ba91f04b8ef50e8a3831ca6b7 100644 --- a/app/src/main/res/xml/locales_config.xml +++ b/app/src/main/res/xml/locales_config.xml @@ -15,4 +15,6 @@<locale android:name="fr"/> <locale android:name="it"/> <locale android:name="pl"/> + <!-- TODO <locale android:name="ru"/> --> + <!-- TODO <locale android:name="ta"/> --> </locale-config> \ No newline at end of file