@@ -24,8 +24,46 @@ import android.widget.LinearLayout
24
24
import android.widget.Spinner
25
25
import android.widget.TextView
26
26
import android.widget.Toast
27
+ import androidx.compose.foundation.clickable
28
+ import androidx.compose.foundation.isSystemInDarkTheme
29
+ import androidx.compose.foundation.layout.Arrangement
30
+ import androidx.compose.foundation.layout.Column
31
+ import androidx.compose.foundation.layout.Row
32
+ import androidx.compose.foundation.layout.fillMaxWidth
33
+ import androidx.compose.foundation.layout.padding
34
+ import androidx.compose.material.icons.Icons
35
+ import androidx.compose.material.icons.filled.KeyboardArrowDown
36
+ import androidx.compose.material.icons.filled.KeyboardArrowUp
37
+ import androidx.compose.material3.Icon
38
+ import androidx.compose.material3.IconButton
39
+ import androidx.compose.material3.LinearProgressIndicator
40
+ import androidx.compose.material3.ListItem
41
+ import androidx.compose.material3.MaterialTheme
42
+ import androidx.compose.material3.Surface
43
+ import androidx.compose.material3.Text
44
+ import androidx.compose.material3.darkColorScheme
45
+ import androidx.compose.material3.lightColorScheme
46
+ import androidx.compose.runtime.Composable
47
+ import androidx.compose.runtime.collectAsState
48
+ import androidx.compose.runtime.getValue
49
+ import androidx.compose.runtime.mutableStateOf
50
+ import androidx.compose.runtime.saveable.rememberSaveable
51
+ import androidx.compose.runtime.setValue
52
+ import androidx.compose.ui.Alignment
53
+ import androidx.compose.ui.Modifier
54
+ import androidx.compose.ui.graphics.Color
55
+ import androidx.compose.ui.platform.LocalUriHandler
56
+ import androidx.compose.ui.platform.ViewCompositionStrategy
57
+ import androidx.compose.ui.res.colorResource
58
+ import androidx.compose.ui.res.stringResource
59
+ import androidx.compose.ui.text.font.FontWeight
60
+ import androidx.compose.ui.text.style.TextAlign
61
+ import androidx.compose.ui.text.style.TextDecoration
62
+ import androidx.compose.ui.unit.dp
63
+ import androidx.compose.ui.unit.sp
27
64
import androidx.fragment.app.Fragment
28
65
import androidx.fragment.app.FragmentTransaction
66
+ import androidx.fragment.app.viewModels
29
67
import com.facebook.drawee.backends.pipeline.Fresco
30
68
import com.facebook.drawee.controller.BaseControllerListener
31
69
import com.facebook.drawee.controller.ControllerListener
@@ -104,6 +142,9 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
104
142
private var isWikipediaButtonDisplayed: Boolean = false
105
143
private val callback: Callback ? = null
106
144
145
+ @Inject
146
+ lateinit var mediaDetailViewModelFactory: MediaDetailViewModel .MediaDetailViewModelProviderFactory
147
+
107
148
@Inject
108
149
lateinit var locationManager: LocationServiceManager
109
150
@@ -145,6 +186,8 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
145
186
@field:Named(" default_preferences" )
146
187
lateinit var applicationKvStore: JsonKvStore
147
188
189
+ private val viewModel: MediaDetailViewModel by viewModels<MediaDetailViewModel > { mediaDetailViewModelFactory }
190
+
148
191
private var initialListTop: Int = 0
149
192
150
193
private var _binding : FragmentMediaDetailBinding ? = null
@@ -316,6 +359,47 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
316
359
binding.coordinateEdit.setOnClickListener { onUpdateCoordinatesClicked() }
317
360
binding.copyWikicode.setOnClickListener { onCopyWikicodeClicked() }
318
361
362
+ binding.fileUsagesComposeView.apply {
363
+ setViewCompositionStrategy(ViewCompositionStrategy .DisposeOnViewTreeLifecycleDestroyed )
364
+ setContent {
365
+ MaterialTheme (
366
+ colorScheme = if (isSystemInDarkTheme()) darkColorScheme(
367
+ primary = colorResource(R .color.primaryDarkColor),
368
+ surface = colorResource(R .color.main_background_dark),
369
+ background = colorResource(R .color.main_background_dark)
370
+ ) else lightColorScheme(
371
+ primary = colorResource(R .color.primaryColor),
372
+ surface = colorResource(R .color.main_background_light),
373
+ background = colorResource(R .color.main_background_light)
374
+ )
375
+ ) {
376
+
377
+ val commonsContainerState by viewModel.commonsContainerState.collectAsState()
378
+ val globalContainerState by viewModel.globalContainerState.collectAsState()
379
+
380
+ Surface {
381
+ Column {
382
+ Text (
383
+ text = stringResource(R .string.file_usages_container_heading),
384
+ modifier = Modifier
385
+ .fillMaxWidth()
386
+ .padding(top = 6 .dp),
387
+ textAlign = TextAlign .Center ,
388
+ fontWeight = FontWeight .SemiBold ,
389
+ fontSize = 16 .sp,
390
+ )
391
+ FileUsagesContainer (
392
+ modifier = Modifier .padding(start = 16 .dp, end = 16 .dp),
393
+ commonsContainerState = commonsContainerState,
394
+ globalContainerState = globalContainerState
395
+ )
396
+ }
397
+ }
398
+
399
+
400
+ }
401
+ }
402
+ }
319
403
320
404
/* *
321
405
* Gets the height of the frame layout as soon as the view is ready and updates aspect ratio
@@ -346,6 +430,16 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
346
430
}
347
431
}
348
432
433
+ private fun fetchFileUsages (fileName : String ) {
434
+ if (viewModel.commonsContainerState.value == MediaDetailViewModel .FileUsagesContainerState .Initial ) {
435
+ viewModel.loadFileUsagesCommons(fileName)
436
+ }
437
+
438
+ if (viewModel.globalContainerState.value == MediaDetailViewModel .FileUsagesContainerState .Initial ) {
439
+ viewModel.loadGlobalFileUsages(fileName)
440
+ }
441
+ }
442
+
349
443
/* *
350
444
* launch zoom acitivity after permission check
351
445
* @param view as ImageView
@@ -422,6 +516,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
422
516
oldWidthOfImageView = binding.mediaDetailScrollView.width
423
517
if (media != null ) {
424
518
displayMediaDetails()
519
+ fetchFileUsages(media?.filename!! )
425
520
}
426
521
}
427
522
}
@@ -677,7 +772,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
677
772
}
678
773
679
774
compositeDisposable.clear()
680
- _binding = null
775
+
681
776
super .onDestroyView()
682
777
}
683
778
@@ -1963,3 +2058,173 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
1963
2058
const val NOMINATING_FOR_DELETION_MEDIA : String = " Nominating for deletion %s"
1964
2059
}
1965
2060
}
2061
+
2062
+ @Composable
2063
+ fun FileUsagesContainer (
2064
+ modifier : Modifier = Modifier ,
2065
+ commonsContainerState : MediaDetailViewModel .FileUsagesContainerState ,
2066
+ globalContainerState : MediaDetailViewModel .FileUsagesContainerState ,
2067
+ ) {
2068
+ var isCommonsListExpanded by rememberSaveable { mutableStateOf(true ) }
2069
+ var isOtherWikisListExpanded by rememberSaveable { mutableStateOf(true ) }
2070
+
2071
+ val uriHandle = LocalUriHandler .current
2072
+
2073
+ Column (modifier = modifier) {
2074
+
2075
+ Row (
2076
+ modifier = Modifier .fillMaxWidth(),
2077
+ verticalAlignment = Alignment .CenterVertically ,
2078
+ horizontalArrangement = Arrangement .SpaceBetween
2079
+ ) {
2080
+
2081
+ Text (
2082
+ text = stringResource(R .string.usages_on_commons_heading),
2083
+ textAlign = TextAlign .Center ,
2084
+ style = MaterialTheme .typography.titleSmall
2085
+ )
2086
+
2087
+ IconButton (onClick = {
2088
+ isCommonsListExpanded = ! isCommonsListExpanded
2089
+ }) {
2090
+ Icon (
2091
+ imageVector = if (isCommonsListExpanded) Icons .Default .KeyboardArrowUp
2092
+ else Icons .Default .KeyboardArrowDown ,
2093
+ contentDescription = null
2094
+ )
2095
+ }
2096
+ }
2097
+
2098
+ if (isCommonsListExpanded) {
2099
+ when (commonsContainerState) {
2100
+ MediaDetailViewModel .FileUsagesContainerState .Loading -> {
2101
+ LinearProgressIndicator ()
2102
+ }
2103
+
2104
+ is MediaDetailViewModel .FileUsagesContainerState .Success -> {
2105
+
2106
+ val data = commonsContainerState.data
2107
+
2108
+ if (data.isNullOrEmpty()) {
2109
+ ListItem (headlineContent = {
2110
+ Text (
2111
+ text = stringResource(R .string.no_usages_found),
2112
+ style = MaterialTheme .typography.titleSmall
2113
+ )
2114
+ })
2115
+ } else {
2116
+ data.forEach { usage ->
2117
+ ListItem (
2118
+ leadingContent = {
2119
+ Text (
2120
+ text = stringResource(R .string.bullet_point),
2121
+ fontWeight = FontWeight .Bold
2122
+ )
2123
+ },
2124
+ headlineContent = {
2125
+ Text (
2126
+ modifier = Modifier .clickable {
2127
+ uriHandle.openUri(usage.link!! )
2128
+ },
2129
+ text = usage.title,
2130
+ style = MaterialTheme .typography.titleSmall.copy(
2131
+ color = Color (0xFF5A6AEC ),
2132
+ textDecoration = TextDecoration .Underline
2133
+ )
2134
+ )
2135
+ })
2136
+ }
2137
+ }
2138
+ }
2139
+
2140
+ is MediaDetailViewModel .FileUsagesContainerState .Error -> {
2141
+ ListItem (headlineContent = {
2142
+ Text (
2143
+ text = commonsContainerState.errorMessage,
2144
+ color = Color .Red ,
2145
+ style = MaterialTheme .typography.titleSmall
2146
+ )
2147
+ })
2148
+ }
2149
+
2150
+ MediaDetailViewModel .FileUsagesContainerState .Initial -> {}
2151
+ }
2152
+ }
2153
+
2154
+
2155
+ Row (
2156
+ modifier = Modifier .fillMaxWidth(),
2157
+ verticalAlignment = Alignment .CenterVertically ,
2158
+ horizontalArrangement = Arrangement .SpaceBetween
2159
+ ) {
2160
+ Text (
2161
+ text = stringResource(R .string.usages_on_other_wikis_heading),
2162
+ textAlign = TextAlign .Center ,
2163
+ style = MaterialTheme .typography.titleSmall
2164
+ )
2165
+
2166
+ IconButton (onClick = {
2167
+ isOtherWikisListExpanded = ! isOtherWikisListExpanded
2168
+ }) {
2169
+ Icon (
2170
+ imageVector = if (isOtherWikisListExpanded) Icons .Default .KeyboardArrowUp
2171
+ else Icons .Default .KeyboardArrowDown ,
2172
+ contentDescription = null
2173
+ )
2174
+ }
2175
+ }
2176
+
2177
+ if (isOtherWikisListExpanded) {
2178
+ when (globalContainerState) {
2179
+ MediaDetailViewModel .FileUsagesContainerState .Loading -> {
2180
+ LinearProgressIndicator ()
2181
+ }
2182
+
2183
+ is MediaDetailViewModel .FileUsagesContainerState .Success -> {
2184
+
2185
+ val data = globalContainerState.data
2186
+
2187
+ if (data.isNullOrEmpty()) {
2188
+ ListItem (headlineContent = {
2189
+ Text (
2190
+ text = stringResource(R .string.no_usages_found),
2191
+ style = MaterialTheme .typography.titleSmall
2192
+ )
2193
+ })
2194
+ } else {
2195
+ data.forEach { usage ->
2196
+ ListItem (
2197
+ leadingContent = {
2198
+ Text (
2199
+ text = stringResource(R .string.bullet_point),
2200
+ fontWeight = FontWeight .Bold
2201
+ )
2202
+ },
2203
+ headlineContent = {
2204
+ Text (
2205
+ text = usage.title,
2206
+ style = MaterialTheme .typography.titleSmall.copy(
2207
+ textDecoration = TextDecoration .Underline
2208
+ )
2209
+ )
2210
+ })
2211
+ }
2212
+ }
2213
+ }
2214
+
2215
+ is MediaDetailViewModel .FileUsagesContainerState .Error -> {
2216
+ ListItem (headlineContent = {
2217
+ Text (
2218
+ text = globalContainerState.errorMessage,
2219
+ color = Color .Red ,
2220
+ style = MaterialTheme .typography.titleSmall
2221
+ )
2222
+ })
2223
+ }
2224
+
2225
+ MediaDetailViewModel .FileUsagesContainerState .Initial -> {}
2226
+ }
2227
+ }
2228
+
2229
+ }
2230
+ }
0 commit comments