v1.7 : destinataires nommés et import depuis le répertoire du téléphone

- Modèle : chaque destinataire = nom + numéro (migration auto des formats
  précédents, numéros existants conservés sans nom)
- Nouvel écran d'édition de groupe : liste des destinataires (nom en gras,
  numéro dessous, suppression à la croix, édition au toucher)
- Import depuis les contacts du téléphone via le sélecteur système
  (ACTION_PICK — aucune permission READ_CONTACTS demandée)
- Saisie manuelle via dialogue nom + numéro avec validation et anti-doublon
- Suppression de groupe avec confirmation
- L'écran principal et la liste des groupes affichent les noms plutôt que
  les numéros quand ils existent

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cosano-star 2026-07-13 15:08:13 +02:00
parent 7e22b54287
commit 39ee74b0bf
11 changed files with 481 additions and 101 deletions

View file

@ -23,8 +23,8 @@ android {
applicationId = "com.example.maposition" applicationId = "com.example.maposition"
minSdk = 21 minSdk = 21
targetSdk = 36 targetSdk = 36
versionCode = 7 versionCode = 8
versionName = "1.6" versionName = "1.7"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }

View file

@ -16,6 +16,7 @@
> >
<activity android:name=".SettingsActivity" /> <activity android:name=".SettingsActivity" />
<activity android:name=".EditGroupActivity" />
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"

View file

@ -0,0 +1,223 @@
package com.example.maposition
import android.content.Intent
import android.os.Bundle
import android.provider.ContactsContract
import android.widget.ImageButton
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
class EditGroupActivity : AppCompatActivity() {
companion object {
const val EXTRA_GROUP = "group_name"
}
private val phoneRegex = Regex("""^\+?[0-9][0-9 .\-]{5,16}$""")
private var originalName: String? = null
private val recipients = mutableListOf<Recipient>()
private lateinit var recipientsList: LinearLayout
private lateinit var nameLayout: TextInputLayout
private lateinit var nameInput: TextInputEditText
private val pickContact =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode != RESULT_OK) return@registerForActivityResult
val uri = result.data?.data ?: return@registerForActivityResult
contentResolver.query(
uri,
arrayOf(
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER
),
null, null, null
)?.use { c ->
if (c.moveToFirst()) {
val name = c.getString(0) ?: ""
val phone = (c.getString(1) ?: "").replace(" ", "")
if (phone.isBlank()) {
Toast.makeText(this, R.string.error_contact_no_phone, Toast.LENGTH_SHORT).show()
} else {
addRecipient(Recipient(name, phone))
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_edit_group)
recipientsList = findViewById(R.id.recipientsList)
nameLayout = findViewById(R.id.groupNameLayout)
nameInput = findViewById(R.id.groupNameInput)
originalName = intent.getStringExtra(EXTRA_GROUP)
val editing = originalName != null
findViewById<TextView>(R.id.editTitle).setText(
if (editing) R.string.group_edit else R.string.group_new
)
if (editing) {
Prefs.getGroups(this).find { it.name == originalName }?.let { g ->
nameInput.setText(g.name)
recipients.addAll(g.recipients)
}
}
findViewById<ImageButton>(R.id.backButton).setOnClickListener { finish() }
findViewById<MaterialButton>(R.id.pickContactButton).setOnClickListener {
pickContact.launch(
Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Phone.CONTENT_URI)
)
}
findViewById<MaterialButton>(R.id.addManualButton).setOnClickListener {
showRecipientDialog(null)
}
findViewById<MaterialButton>(R.id.saveGroupButton).setOnClickListener { saveGroup() }
val deleteButton = findViewById<MaterialButton>(R.id.deleteGroupButton)
if (editing) {
deleteButton.setOnClickListener { confirmDelete() }
} else {
deleteButton.visibility = android.view.View.GONE
}
renderRecipients()
}
// --- Liste des destinataires ---
private fun addRecipient(r: Recipient) {
if (recipients.any { it.phone == r.phone }) {
Toast.makeText(this, R.string.error_dup_phone, Toast.LENGTH_SHORT).show()
return
}
recipients.add(r)
renderRecipients()
}
private fun renderRecipients() {
recipientsList.removeAllViews()
if (recipients.isEmpty()) {
val empty = TextView(this).apply {
text = getString(R.string.no_recipients_yet)
setPadding(8, 16, 8, 16)
}
recipientsList.addView(empty)
return
}
recipients.forEachIndexed { index, r ->
val item = layoutInflater.inflate(R.layout.item_recipient, recipientsList, false)
item.findViewById<TextView>(R.id.recipientName).text = r.name.ifBlank { getString(R.string.unnamed_recipient) }
item.findViewById<TextView>(R.id.recipientPhone).text = r.phone
item.findViewById<ImageButton>(R.id.removeButton).setOnClickListener {
recipients.removeAt(index)
renderRecipients()
}
item.setOnClickListener { showRecipientDialog(index) }
recipientsList.addView(item)
}
}
private fun showRecipientDialog(index: Int?) {
val existing = index?.let { recipients[it] }
val view = layoutInflater.inflate(R.layout.dialog_recipient, null)
val rNameInput = view.findViewById<TextInputEditText>(R.id.recipientNameInput)
val rPhoneLayout = view.findViewById<TextInputLayout>(R.id.recipientPhoneLayout)
val rPhoneInput = view.findViewById<TextInputEditText>(R.id.recipientPhoneInput)
if (existing != null) {
rNameInput.setText(existing.name)
rPhoneInput.setText(existing.phone)
}
val dialog = MaterialAlertDialogBuilder(this)
.setTitle(if (existing == null) R.string.recipient_new else R.string.recipient_edit)
.setView(view)
.setPositiveButton(R.string.save, null)
.setNegativeButton(android.R.string.cancel, null)
.create()
dialog.setOnShowListener {
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
val name = rNameInput.text.toString().trim()
val phone = rPhoneInput.text.toString().trim()
rPhoneLayout.error = null
when {
phone.isEmpty() || !phoneRegex.matches(phone) ->
rPhoneLayout.error = getString(R.string.error_bad_phone, phone)
recipients.anyIndexed(index) { it.phone == phone } ->
rPhoneLayout.error = getString(R.string.error_dup_phone)
else -> {
val r = Recipient(name, phone)
if (index != null) recipients[index] = r else recipients.add(r)
renderRecipients()
dialog.dismiss()
}
}
}
}
dialog.show()
}
/** any() en ignorant l'élément en cours d'édition. */
private fun List<Recipient>.anyIndexed(skip: Int?, pred: (Recipient) -> Boolean): Boolean =
filterIndexed { i, _ -> i != skip }.any(pred)
// --- Enregistrement / suppression du groupe ---
private fun saveGroup() {
val name = nameInput.text.toString().trim()
nameLayout.error = null
val groups = Prefs.getGroups(this)
when {
name.isEmpty() -> {
nameLayout.error = getString(R.string.error_no_group_name)
return
}
groups.any { it.name == name && it.name != originalName } -> {
nameLayout.error = getString(R.string.error_dup_group)
return
}
recipients.isEmpty() -> {
Toast.makeText(this, R.string.error_no_recipients, Toast.LENGTH_SHORT).show()
return
}
}
val idx = groups.indexOfFirst { it.name == originalName }
val group = Group(name, recipients.toList())
if (idx >= 0) groups[idx] = group else groups.add(group)
Prefs.saveGroups(this, groups)
if (originalName != null && Prefs.getActiveGroupName(this) == originalName) {
Prefs.setActiveGroupName(this, name)
}
finish()
}
private fun confirmDelete() {
MaterialAlertDialogBuilder(this)
.setMessage(getString(R.string.delete_group_confirm, originalName))
.setPositiveButton(R.string.delete) { _, _ ->
val groups = Prefs.getGroups(this)
groups.removeAll { it.name == originalName }
Prefs.saveGroups(this, groups)
finish()
}
.setNegativeButton(android.R.string.cancel, null)
.show()
}
}

View file

@ -149,7 +149,7 @@ class MainActivity : AppCompatActivity() {
} }
groupChips.addView(chip) groupChips.addView(chip)
} }
recipientsText.text = active.phones.joinToString(", ") recipientsText.text = active.displayList()
} }
// --- Déclenchement de l'envoi --- // --- Déclenchement de l'envoi ---

View file

@ -4,7 +4,15 @@ import android.content.Context
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
data class Group(val name: String, val phones: List<String>) data class Recipient(val name: String, val phone: String)
data class Group(val name: String, val recipients: List<Recipient>) {
val phones: List<String> get() = recipients.map { it.phone }
/** "Alice, Bob, 0611…" — le nom si renseigné, sinon le numéro. */
fun displayList(): String =
recipients.joinToString(", ") { it.name.ifBlank { it.phone } }
}
object Prefs { object Prefs {
private const val FILE = "MaPositionPrefs" private const val FILE = "MaPositionPrefs"
@ -24,17 +32,28 @@ object Prefs {
val arr = JSONArray(raw) val arr = JSONArray(raw)
for (i in 0 until arr.length()) { for (i in 0 until arr.length()) {
val o = arr.getJSONObject(i) val o = arr.getJSONObject(i)
val phones = mutableListOf<String>() val recipients = mutableListOf<Recipient>()
val pa = o.getJSONArray("phones") val ra = o.optJSONArray("recipients")
for (j in 0 until pa.length()) phones.add(pa.getString(j)) if (ra != null) {
groups.add(Group(o.getString("name"), phones)) for (j in 0 until ra.length()) {
val ro = ra.getJSONObject(j)
recipients.add(Recipient(ro.optString("name", ""), ro.getString("phone")))
} }
} else { } else {
// Migration depuis l'ancien format : liste unique de numéros // Migration depuis le format v1.4 : numéros sans nom
val pa = o.optJSONArray("phones")
if (pa != null) {
for (j in 0 until pa.length()) recipients.add(Recipient("", pa.getString(j)))
}
}
groups.add(Group(o.getString("name"), recipients))
}
} else {
// Migration depuis le format v1.0-1.3 : liste unique de numéros
val legacy = sp(ctx).getString("phones", "") ?: "" val legacy = sp(ctx).getString("phones", "") ?: ""
val phones = legacy.split(";").map { it.trim() }.filter { it.isNotEmpty() } val phones = legacy.split(";").map { it.trim() }.filter { it.isNotEmpty() }
if (phones.isNotEmpty()) { if (phones.isNotEmpty()) {
groups.add(Group(ctx.getString(R.string.default_group_name), phones)) groups.add(Group(ctx.getString(R.string.default_group_name), phones.map { Recipient("", it) }))
saveGroups(ctx, groups) saveGroups(ctx, groups)
} }
} }
@ -44,7 +63,11 @@ object Prefs {
fun saveGroups(ctx: Context, groups: List<Group>) { fun saveGroups(ctx: Context, groups: List<Group>) {
val arr = JSONArray() val arr = JSONArray()
groups.forEach { g -> groups.forEach { g ->
arr.put(JSONObject().put("name", g.name).put("phones", JSONArray(g.phones))) val ra = JSONArray()
g.recipients.forEach { r ->
ra.put(JSONObject().put("name", r.name).put("phone", r.phone))
}
arr.put(JSONObject().put("name", g.name).put("recipients", ra))
} }
sp(ctx).edit().putString("groups", arr.toString()).apply() sp(ctx).edit().putString("groups", arr.toString()).apply()
} }

View file

@ -1,22 +1,18 @@
package com.example.maposition package com.example.maposition
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.view.View
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
class SettingsActivity : AppCompatActivity() { class SettingsActivity : AppCompatActivity() {
private val phoneRegex = Regex("""^\+?[0-9][0-9 .\-]{5,16}$""")
private lateinit var groupsList: LinearLayout private lateinit var groupsList: LinearLayout
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -36,8 +32,13 @@ class SettingsActivity : AppCompatActivity() {
}) })
findViewById<ImageButton>(R.id.backButton).setOnClickListener { finish() } findViewById<ImageButton>(R.id.backButton).setOnClickListener { finish() }
findViewById<MaterialButton>(R.id.addGroupButton).setOnClickListener { showGroupDialog(null) } findViewById<MaterialButton>(R.id.addGroupButton).setOnClickListener {
startActivity(Intent(this, EditGroupActivity::class.java))
}
}
override fun onResume() {
super.onResume()
renderGroups() renderGroups()
} }
@ -63,76 +64,14 @@ class SettingsActivity : AppCompatActivity() {
g.name g.name
} }
item.findViewById<TextView>(R.id.groupName).text = title item.findViewById<TextView>(R.id.groupName).text = title
item.findViewById<TextView>(R.id.groupPhones).text = g.phones.joinToString(", ") item.findViewById<TextView>(R.id.groupPhones).text = g.displayList()
item.setOnClickListener { showGroupDialog(g) } item.setOnClickListener {
startActivity(
Intent(this, EditGroupActivity::class.java)
.putExtra(EditGroupActivity.EXTRA_GROUP, g.name)
)
}
groupsList.addView(item) groupsList.addView(item)
} }
} }
private fun showGroupDialog(existing: Group?) {
val view = layoutInflater.inflate(R.layout.dialog_group, null)
val nameLayout = view.findViewById<TextInputLayout>(R.id.groupNameLayout)
val nameInput = view.findViewById<TextInputEditText>(R.id.groupNameInput)
val phonesLayout = view.findViewById<TextInputLayout>(R.id.groupPhonesLayout)
val phonesInput = view.findViewById<TextInputEditText>(R.id.groupPhonesInput)
if (existing != null) {
nameInput.setText(existing.name)
phonesInput.setText(existing.phones.joinToString(";"))
}
val builder = MaterialAlertDialogBuilder(this)
.setTitle(if (existing == null) R.string.group_new else R.string.group_edit)
.setView(view)
.setPositiveButton(R.string.save, null)
.setNegativeButton(android.R.string.cancel, null)
if (existing != null) builder.setNeutralButton(R.string.delete, null)
val dialog = builder.create()
dialog.setOnShowListener {
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
val name = nameInput.text.toString().trim()
val phones = phonesInput.text.toString()
.split(";").map { it.trim() }.filter { it.isNotEmpty() }
nameLayout.error = null
phonesLayout.error = null
val groups = Prefs.getGroups(this)
when {
name.isEmpty() ->
nameLayout.error = getString(R.string.error_no_group_name)
groups.any { it.name == name && it.name != existing?.name } ->
nameLayout.error = getString(R.string.error_dup_group)
phones.isEmpty() ->
phonesLayout.error = getString(R.string.error_no_phone)
phones.any { !phoneRegex.matches(it) } ->
phonesLayout.error = getString(
R.string.error_bad_phone,
phones.first { !phoneRegex.matches(it) }
)
else -> {
val idx = groups.indexOfFirst { it.name == existing?.name }
if (idx >= 0) groups[idx] = Group(name, phones)
else groups.add(Group(name, phones))
Prefs.saveGroups(this, groups)
if (existing != null && Prefs.getActiveGroupName(this) == existing.name) {
Prefs.setActiveGroupName(this, name)
}
renderGroups()
dialog.dismiss()
}
}
}
if (existing != null) {
dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener {
val groups = Prefs.getGroups(this)
groups.removeAll { it.name == existing.name }
Prefs.saveGroups(this, groups)
renderGroups()
dialog.dismiss()
}
}
}
dialog.show()
}
} }

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z" />
</vector>

View file

@ -0,0 +1,125 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.07"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/bg_watermark" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageButton
android:id="@+id/backButton"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/back"
android:src="@drawable/ic_back"
app:tint="?attr/colorOnSurface" />
<TextView
android:id="@+id/editTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textSize="24sp"
android:textStyle="bold" />
</LinearLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/groupNameLayout"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:hint="@string/group_name_hint">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/groupNameInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:maxLength="24" />
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="@string/recipients_title"
android:textSize="18sp"
android:textStyle="bold" />
<LinearLayout
android:id="@+id/recipientsList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:orientation="vertical" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/pickContactButton"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="6dp"
android:layout_weight="1"
android:text="@string/add_from_contacts" />
<com.google.android.material.button.MaterialButton
android:id="@+id/addManualButton"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_weight="1"
android:text="@string/add_manual" />
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/saveGroupButton"
android:layout_width="match_parent"
android:layout_height="52dp"
android:layout_marginTop="24dp"
android:text="@string/save"
app:backgroundTint="@color/brand_blue"
app:cornerRadius="14dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/deleteGroupButton"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/delete_group"
android:textColor="?attr/colorError" />
</LinearLayout>
</ScrollView>
</FrameLayout>

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
@ -9,34 +8,33 @@
android:paddingEnd="24dp"> android:paddingEnd="24dp">
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/groupNameLayout" android:id="@+id/recipientNameLayout"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox" style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/group_name_hint"> android:hint="@string/recipient_name_hint">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/groupNameInput" android:id="@+id/recipientNameInput"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="text" android:inputType="textPersonName"
android:maxLength="24" /> android:maxLength="40" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/groupPhonesLayout" android:id="@+id/recipientPhoneLayout"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox" style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:hint="@string/phones_hint" android:hint="@string/recipient_phone_hint">
app:helperText="@string/phones_helper">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/groupPhonesInput" android:id="@+id/recipientPhoneInput"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="text" /> android:inputType="phone" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
</LinearLayout> </LinearLayout>

View file

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:clickable="true"
android:focusable="true"
app:cardCornerRadius="12dp"
app:cardElevation="1dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="14dp"
android:paddingTop="10dp"
android:paddingEnd="6dp"
android:paddingBottom="10dp">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/recipientName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="15sp"
android:textStyle="bold" />
<TextView
android:id="@+id/recipientPhone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?attr/colorOnSurfaceVariant"
android:textSize="14sp" />
</LinearLayout>
<ImageButton
android:id="@+id/removeButton"
android:layout_width="44dp"
android:layout_height="44dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/delete"
android:src="@drawable/ic_close"
app:tint="?attr/colorOnSurfaceVariant" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>

View file

@ -19,6 +19,20 @@
<string name="add_group">+ Ajouter un groupe</string> <string name="add_group">+ Ajouter un groupe</string>
<string name="no_groups">Aucun groupe pour l\'instant.</string> <string name="no_groups">Aucun groupe pour l\'instant.</string>
<string name="delete">Supprimer</string> <string name="delete">Supprimer</string>
<string name="delete_group">Supprimer ce groupe</string>
<string name="delete_group_confirm">Supprimer le groupe « %1$s » ?</string>
<string name="recipients_title">Destinataires du groupe</string>
<string name="no_recipients_yet">Aucun destinataire pour l\'instant.</string>
<string name="add_from_contacts">📇 Depuis le répertoire</string>
<string name="add_manual">✏️ Saisie manuelle</string>
<string name="recipient_new">Ajouter un destinataire</string>
<string name="recipient_edit">Modifier le destinataire</string>
<string name="recipient_name_hint">Nom (ex : Maman)</string>
<string name="recipient_phone_hint">Numéro de téléphone</string>
<string name="unnamed_recipient">Sans nom</string>
<string name="error_dup_phone">Ce numéro est déjà dans le groupe</string>
<string name="error_no_recipients">Ajoutez au moins un destinataire</string>
<string name="error_contact_no_phone">Ce contact n\'a pas de numéro</string>
<string name="error_no_group_name">Donnez un nom au groupe</string> <string name="error_no_group_name">Donnez un nom au groupe</string>
<string name="error_dup_group">Un groupe porte déjà ce nom</string> <string name="error_dup_group">Un groupe porte déjà ce nom</string>
@ -41,10 +55,6 @@
<!-- Paramètres --> <!-- Paramètres -->
<string name="name_hint">Votre nom</string> <string name="name_hint">Votre nom</string>
<string name="name_helper">Apparaîtra dans le SMS envoyé</string> <string name="name_helper">Apparaîtra dans le SMS envoyé</string>
<string name="phones_hint">Numéros destinataires</string>
<string name="phones_helper">Plusieurs numéros : séparez-les par un point-virgule. Ex : 0611223344;0622334455</string>
<string name="save">Enregistrer</string> <string name="save">Enregistrer</string>
<string name="saved">Paramètres enregistrés</string>
<string name="error_no_phone">Saisissez au moins un numéro</string>
<string name="error_bad_phone">Numéro invalide : %1$s</string> <string name="error_bad_phone">Numéro invalide : %1$s</string>
</resources> </resources>