Skip to content

Commit e44167e

Browse files
committed
Give the app its own mark, and make the screen read as an Android app
The launcher icon was a hand-drawn vector — a blue square with a page outline — unrelated to the mark the desktop builds ship. Every density is now generated from .github/assets/logo.png by android/tools/generate_launcher_icons.py, with a monochrome layer so Android 13 themed icons work, and the artwork sized into the 66dp safe zone so a circular launcher does not clip the arrows. The header shows the same mark instead of a generic PDF glyph. The rest is a UI that was ported from the desktop window without being adapted: - The colour scheme set seven of Material 3's ~30 roles and let the rest fall back to the default baseline, which is lavender. surfaceVariant, error and onSurfaceVariant are all used by the About panel, the log viewer and menus, so those were purple inside a blue and grey app. Both schemes are complete now. - Queue rows picked their status colour from the Light constants regardless of theme. In dark mode a finished file was #1A7F37 on #161B22 — a dark green on near-black. The Dark constants were sitting in Color.kt unread. - bodySmall was Monospace. One of its ten callers wanted that; the other nine were file names, folder names and button labels set in a typewriter face. - Body text ran a step small throughout, sized for a monitor rather than a hand. - Touch targets: 36dp icon buttons in the header, a 24dp box holding a ✕ to remove a queue row, a 32dp "Mở PDF". The minimum is 48dp. - The file picker was a "drop zone" with a ⤓ glyph. There is nothing to drag on a phone, tapping it did nothing, and that character is not in every font. - The footer printed the raw output location, which for a folder picked through the storage access framework is a content:// URI that wraps over three lines. - The window theme was the platform default, so a cold start flashed a colour Compose then repainted. Dynamic colour is deliberately not adopted: the blue is shared with the Windows and macOS builds, and following the wallpaper would break that on one platform of the three.
1 parent 877cabc commit e44167e

41 files changed

Lines changed: 859 additions & 461 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

android/app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
android:label="@string/app_name"
1818
android:roundIcon="@mipmap/ic_launcher_round"
1919
android:supportsRtl="true"
20-
android:theme="@android:style/Theme.Material.NoActionBar">
20+
android:theme="@style/Theme.PDFTranslate">
2121

2222
<activity
2323
android:name=".MainActivity"

android/app/src/main/java/com/vitranslate/pdf/MainActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ fun MainScreen(
189189
}
190190
)
191191

192-
DropZoneView(
192+
FilePickerView(
193193
hasFiles = queueItems.isNotEmpty(),
194194
onPickFiles = onPickFiles,
195195
onPickDirectory = onPickDirectory

android/app/src/main/java/com/vitranslate/pdf/ui/components/ControlsView.kt

Lines changed: 95 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ import android.net.Uri
44
import androidx.compose.foundation.clickable
55
import androidx.compose.foundation.layout.*
66
import androidx.compose.foundation.shape.RoundedCornerShape
7+
import androidx.compose.material.icons.Icons
8+
import androidx.compose.material.icons.filled.FolderOpen
79
import androidx.compose.material3.*
810
import androidx.compose.runtime.*
911
import androidx.compose.ui.Alignment
1012
import androidx.compose.ui.Modifier
13+
import androidx.compose.ui.draw.clip
1114
import androidx.compose.ui.platform.LocalContext
1215
import androidx.compose.ui.text.font.FontWeight
1316
import androidx.compose.ui.text.style.TextOverflow
@@ -50,122 +53,104 @@ fun ControlsView(
5053
Card(
5154
modifier = Modifier
5255
.fillMaxWidth()
53-
.padding(horizontal = 24.dp, vertical = 12.dp),
54-
shape = RoundedCornerShape(12.dp),
55-
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface)
56+
.padding(horizontal = 16.dp, vertical = 12.dp),
57+
shape = RoundedCornerShape(16.dp),
58+
colors = CardDefaults.cardColors(
59+
containerColor = MaterialTheme.colorScheme.surfaceContainerLow
60+
)
5661
) {
57-
Column(
58-
modifier = Modifier
59-
.fillMaxWidth()
60-
.padding(16.dp)
61-
) {
62-
Row(
63-
modifier = Modifier.fillMaxWidth(),
64-
verticalAlignment = Alignment.CenterVertically,
65-
horizontalArrangement = Arrangement.SpaceBetween
66-
) {
67-
Column(modifier = Modifier.weight(1f)) {
68-
Text(
69-
text = "Dịch sang",
70-
style = MaterialTheme.typography.bodyMedium,
71-
color = MaterialTheme.colorScheme.onSurface,
72-
modifier = Modifier.padding(bottom = 6.dp)
73-
)
62+
Column(modifier = Modifier.padding(16.dp)) {
63+
Text(
64+
text = "Dịch sang",
65+
style = MaterialTheme.typography.labelLarge,
66+
color = MaterialTheme.colorScheme.onSurfaceVariant
67+
)
68+
Spacer(Modifier.height(6.dp))
7469

75-
ExposedDropdownMenuBox(
76-
expanded = expanded,
77-
onExpandedChange = { if (!isTranslating) expanded = !expanded }
78-
) {
79-
OutlinedTextField(
80-
value = selectedLanguage.name,
81-
onValueChange = {},
82-
readOnly = true,
83-
enabled = !isTranslating,
84-
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
85-
modifier = Modifier
86-
.menuAnchor()
87-
.fillMaxWidth(0.9f),
88-
shape = RoundedCornerShape(8.dp)
89-
)
70+
// The language picker used to share a row with the action button,
71+
// which left it about half the screen wide on a phone. Full width
72+
// here, with the action below it.
73+
ExposedDropdownMenuBox(
74+
expanded = expanded,
75+
onExpandedChange = { if (!isTranslating) expanded = !expanded }
76+
) {
77+
OutlinedTextField(
78+
value = selectedLanguage.name,
79+
onValueChange = {},
80+
readOnly = true,
81+
enabled = !isTranslating,
82+
singleLine = true,
83+
textStyle = MaterialTheme.typography.bodyMedium,
84+
trailingIcon = {
85+
ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
86+
},
87+
modifier = Modifier
88+
.menuAnchor(MenuAnchorType.PrimaryNotEditable, enabled = !isTranslating)
89+
.fillMaxWidth(),
90+
shape = RoundedCornerShape(12.dp)
91+
)
9092

91-
ExposedDropdownMenu(
92-
expanded = expanded,
93-
onDismissRequest = { expanded = false }
94-
) {
95-
TargetLanguage.SUPPORTED_LANGUAGES.forEach { language ->
96-
DropdownMenuItem(
97-
text = { Text(language.name) },
98-
onClick = {
99-
onLanguageSelected(language)
100-
expanded = false
101-
}
102-
)
93+
ExposedDropdownMenu(
94+
expanded = expanded,
95+
onDismissRequest = { expanded = false }
96+
) {
97+
TargetLanguage.SUPPORTED_LANGUAGES.forEach { language ->
98+
DropdownMenuItem(
99+
text = {
100+
Text(language.name, style = MaterialTheme.typography.bodyMedium)
101+
},
102+
onClick = {
103+
onLanguageSelected(language)
104+
expanded = false
103105
}
104-
}
106+
)
105107
}
106108
}
107-
108-
// A long run is the normal case, so the same button has to be
109-
// the way out of it; a disabled "Đang dịch…" left no way to stop.
110-
Button(
111-
onClick = { if (isTranslating) onCancelTranslation() else onStartTranslation() },
112-
shape = RoundedCornerShape(8.dp),
113-
colors = ButtonDefaults.buttonColors(
114-
containerColor = if (isTranslating) {
115-
MaterialTheme.colorScheme.error
116-
} else {
117-
MaterialTheme.colorScheme.primary
118-
}
119-
),
120-
modifier = Modifier
121-
.height(48.dp)
122-
.padding(start = 8.dp)
123-
) {
124-
Text(
125-
text = if (isTranslating) "Huỷ" else "Dịch",
126-
style = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.Bold)
127-
)
128-
}
129109
}
130110

131-
Spacer(modifier = Modifier.height(10.dp))
111+
Spacer(Modifier.height(12.dp))
132112

133113
Row(
134114
modifier = Modifier.fillMaxWidth(),
135-
verticalAlignment = Alignment.CenterVertically,
136-
horizontalArrangement = Arrangement.SpaceBetween
115+
verticalAlignment = Alignment.CenterVertically
137116
) {
117+
Icon(
118+
imageVector = Icons.Default.FolderOpen,
119+
contentDescription = null,
120+
tint = MaterialTheme.colorScheme.onSurfaceVariant,
121+
modifier = Modifier.size(18.dp)
122+
)
123+
Spacer(Modifier.width(8.dp))
138124
Column(modifier = Modifier.weight(1f)) {
139125
Text(
140-
text = "Thư mục lưu file:",
141-
style = MaterialTheme.typography.bodySmall,
142-
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f)
126+
text = "Lưu vào",
127+
style = MaterialTheme.typography.labelMedium,
128+
color = MaterialTheme.colorScheme.onSurfaceVariant
143129
)
144130
Text(
145131
text = folderDisplayName,
146-
style = MaterialTheme.typography.bodySmall,
147-
fontWeight = FontWeight.SemiBold,
132+
style = MaterialTheme.typography.bodyMedium,
133+
color = MaterialTheme.colorScheme.onSurface,
148134
maxLines = 1,
149135
overflow = TextOverflow.Ellipsis
150136
)
151137
}
152-
153-
OutlinedButton(
138+
TextButton(
154139
onClick = onPickSaveDirectory,
155-
enabled = !isTranslating,
156-
shape = RoundedCornerShape(8.dp),
157-
modifier = Modifier.padding(start = 8.dp)
140+
enabled = !isTranslating
158141
) {
159-
Text("Chọn thư mục", style = MaterialTheme.typography.bodySmall)
142+
Text("Đổi", style = MaterialTheme.typography.labelLarge)
160143
}
161144
}
162145

163-
Spacer(modifier = Modifier.height(6.dp))
164-
146+
// The whole row is the target rather than just the checkbox, and it
147+
// is tall enough to hit without aiming.
165148
Row(
166149
verticalAlignment = Alignment.CenterVertically,
167150
modifier = Modifier
168151
.fillMaxWidth()
152+
.heightIn(min = 48.dp)
153+
.clip(RoundedCornerShape(8.dp))
169154
.clickable(enabled = !isTranslating) { onOverwriteChange(!overwrite) }
170155
) {
171156
Checkbox(
@@ -179,6 +164,30 @@ fun ControlsView(
179164
color = MaterialTheme.colorScheme.onSurface
180165
)
181166
}
167+
168+
Spacer(Modifier.height(4.dp))
169+
170+
// A long run is the normal case, so the same button has to be the
171+
// way out of it; a disabled "Đang dịch…" left no way to stop.
172+
Button(
173+
onClick = { if (isTranslating) onCancelTranslation() else onStartTranslation() },
174+
shape = RoundedCornerShape(12.dp),
175+
colors = ButtonDefaults.buttonColors(
176+
containerColor = if (isTranslating) {
177+
MaterialTheme.colorScheme.error
178+
} else {
179+
MaterialTheme.colorScheme.primary
180+
}
181+
),
182+
modifier = Modifier
183+
.fillMaxWidth()
184+
.heightIn(min = 52.dp)
185+
) {
186+
Text(
187+
text = if (isTranslating) "Huỷ dịch" else "Bắt đầu dịch",
188+
style = MaterialTheme.typography.labelLarge.copy(fontWeight = FontWeight.Bold)
189+
)
190+
}
182191
}
183192
}
184193
}

android/app/src/main/java/com/vitranslate/pdf/ui/components/DropZoneView.kt

Lines changed: 0 additions & 81 deletions
This file was deleted.

0 commit comments

Comments
 (0)