diff --git a/android-app/.idea/dictionaries/hossain.xml b/android-app/.idea/dictionaries/hossain.xml
new file mode 100644
index 0000000..61ca0ea
--- /dev/null
+++ b/android-app/.idea/dictionaries/hossain.xml
@@ -0,0 +1,7 @@
+
+
+
+ hashtag
+
+
+
\ No newline at end of file
diff --git a/android-app/app/build.gradle b/android-app/app/build.gradle
index 9684868..6779f8e 100644
--- a/android-app/app/build.gradle
+++ b/android-app/app/build.gradle
@@ -20,8 +20,8 @@ android {
applicationId "com.blacklivesmatter.policebrutality"
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
- versionCode 5
- versionName "1.4"
+ versionCode 6
+ versionName "1.5"
resConfigs "en"
@@ -90,7 +90,6 @@ dependencies {
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-rc01"
// Firebase
- implementation "com.google.firebase:firebase-analytics:$rootProject.firebaseAnalyticsVersion"
implementation "com.google.firebase:firebase-analytics-ktx:$rootProject.firebaseAnalyticsVersion"
implementation "com.google.firebase:firebase-crashlytics:$rootProject.firebaseCrashlyticsVersion"
diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/analytics/Analytics.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/analytics/Analytics.kt
new file mode 100644
index 0000000..d4f3a14
--- /dev/null
+++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/analytics/Analytics.kt
@@ -0,0 +1,53 @@
+package com.blacklivesmatter.policebrutality.analytics
+
+import android.app.Activity
+
+/**
+ * Interface to log events for the app.
+ */
+interface Analytics {
+ companion object {
+ const val SCREEN_REPORT_INCIDENT = "ReportNewIncident"
+ const val SCREEN_INCIDENT_LOCATION = "IncidentLocations"
+ const val SCREEN_INCIDENT_LIST_BY_DATE = "IncidentsByDate"
+ const val SCREEN_INCIDENT_LIST_BY_LOCATION = "IncidentsByLocation"
+ const val SCREEN_INCIDENT_DATE_FILTER = "FilterIncidentsByDate"
+ const val SCREEN_MORE_INFO = "MoreInformation"
+ const val SCREEN_ABOUT_APP = "AboutApplication"
+
+ const val ACTION_INCIDENT_REFRESH = "RefreshIncidents"
+ const val ACTION_INCIDENT_REPORT_NEW = "MakeIncidentReport"
+ const val ACTION_SHARE_APP = "ShareApplication"
+
+ const val CONTENT_TYPE_LOCATION = "TypeLocation"
+ const val CONTENT_TYPE_INCIDENT = "TypeIncident"
+ const val CONTENT_TYPE_INCIDENT_SHARE = "TypeShareIncident"
+ const val CONTENT_TYPE_HASHTAG = "TypeHashtag"
+ const val CONTENT_TYPE_PB2020_LINK = "TypePBLink"
+ }
+
+ /**
+ * Log event when a user views a page.
+ */
+ fun logPageView(activity: Activity, screenName: String)
+
+ /**
+ * Log event when a user searches in the app
+ */
+ fun logSearch(searchTerm: String)
+
+ /**
+ * Log event when a user has selected content in an app
+ */
+ fun logSelectItem(type: String, id: String, name: String)
+
+ /**
+ * Log event when a user has shared content in an app
+ */
+ fun logShare(type: String, id: String)
+
+ /**
+ * Log event when a user action is taken.
+ */
+ fun logEvent(name: String)
+}
diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/analytics/AppAnalytics.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/analytics/AppAnalytics.kt
new file mode 100644
index 0000000..8342adb
--- /dev/null
+++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/analytics/AppAnalytics.kt
@@ -0,0 +1,49 @@
+package com.blacklivesmatter.policebrutality.analytics
+
+import android.app.Activity
+import com.google.firebase.analytics.FirebaseAnalytics
+import com.google.firebase.analytics.FirebaseAnalytics.Event
+import com.google.firebase.analytics.FirebaseAnalytics.Event.SEARCH
+import com.google.firebase.analytics.FirebaseAnalytics.Param.CONTENT_TYPE
+import com.google.firebase.analytics.FirebaseAnalytics.Param.ITEM_ID
+import com.google.firebase.analytics.FirebaseAnalytics.Param.ITEM_NAME
+import com.google.firebase.analytics.FirebaseAnalytics.Param.SEARCH_TERM
+import com.google.firebase.analytics.ktx.logEvent
+import javax.inject.Inject
+
+/**
+ * App analytics that uses [FirebaseAnalytics] as backbone.
+ * See https://firebase.google.com/docs/analytics/get-started?platform=android
+ */
+class AppAnalytics @Inject constructor(
+ private val firebaseAnalytics: FirebaseAnalytics
+) : Analytics {
+ override fun logPageView(activity: Activity, screenName: String) {
+ firebaseAnalytics.setCurrentScreen(activity, screenName, null)
+ }
+
+ override fun logSearch(searchTerm: String) {
+ firebaseAnalytics.logEvent(SEARCH) {
+ param(SEARCH_TERM, searchTerm)
+ }
+ }
+
+ override fun logSelectItem(type: String, id: String, name: String) {
+ firebaseAnalytics.logEvent(Event.SELECT_CONTENT) {
+ param(ITEM_ID, id)
+ param(ITEM_NAME, name)
+ param(CONTENT_TYPE, type)
+ }
+ }
+
+ override fun logShare(type: String, id: String) {
+ firebaseAnalytics.logEvent(Event.SHARE) {
+ param(ITEM_ID, id)
+ param(CONTENT_TYPE, type)
+ }
+ }
+
+ override fun logEvent(name: String) {
+ firebaseAnalytics.logEvent(name, null)
+ }
+}
diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/component/AppComponent.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/component/AppComponent.kt
index a6476c0..ae30165 100644
--- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/component/AppComponent.kt
+++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/component/AppComponent.kt
@@ -3,6 +3,7 @@ package com.blacklivesmatter.policebrutality.di.component
import android.app.Application
import com.blacklivesmatter.policebrutality.BrutalityIncidentApplication
import com.blacklivesmatter.policebrutality.di.module.ActivityModule
+import com.blacklivesmatter.policebrutality.di.module.AnalyticsModule
import com.blacklivesmatter.policebrutality.di.module.ApiModule
import com.blacklivesmatter.policebrutality.di.module.AppModule
import com.blacklivesmatter.policebrutality.di.module.DaoModule
@@ -19,12 +20,13 @@ import javax.inject.Singleton
@Component(
modules = [
AndroidSupportInjectionModule::class,
- AppModule::class,
ActivityModule::class,
- ViewModelModule::class,
- RepositoryModule::class,
- DaoModule::class,
+ AnalyticsModule::class,
ApiModule::class,
+ AppModule::class,
+ DaoModule::class,
+ RepositoryModule::class,
+ ViewModelModule::class,
WorkerModule::class
]
)
diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/AnalyticsModule.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/AnalyticsModule.kt
new file mode 100644
index 0000000..3afabdf
--- /dev/null
+++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/AnalyticsModule.kt
@@ -0,0 +1,28 @@
+package com.blacklivesmatter.policebrutality.di.module
+
+import android.content.Context
+import com.blacklivesmatter.policebrutality.analytics.Analytics
+import com.blacklivesmatter.policebrutality.analytics.AppAnalytics
+import com.google.firebase.analytics.FirebaseAnalytics
+import dagger.Module
+import dagger.Provides
+import timber.log.Timber
+import javax.inject.Singleton
+
+@Module
+class AnalyticsModule {
+ @Singleton
+ @Provides
+ fun provideAnalytics(context: Context): FirebaseAnalytics {
+ // https://firebase.google.com/docs/analytics/get-started?platform=android
+ val instance = FirebaseAnalytics.getInstance(context)
+ Timber.d("Providing firebase analytics instance: $instance")
+ return instance
+ }
+
+ @Singleton
+ @Provides
+ fun provideAppAnalytics(firebaseAnalytics: FirebaseAnalytics): Analytics {
+ return AppAnalytics(firebaseAnalytics)
+ }
+}
diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/incident/IncidentViewModel.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/incident/IncidentViewModel.kt
index 217ac37..8a97508 100644
--- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/incident/IncidentViewModel.kt
+++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/incident/IncidentViewModel.kt
@@ -1,27 +1,55 @@
package com.blacklivesmatter.policebrutality.ui.incident
+import android.content.SharedPreferences
import androidx.databinding.ObservableField
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
+import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.blacklivesmatter.policebrutality.data.IncidentRepository
import com.blacklivesmatter.policebrutality.data.model.Incident
+import com.blacklivesmatter.policebrutality.ui.extensions.LiveEvent
import timber.log.Timber
import javax.inject.Inject
class IncidentViewModel @Inject constructor(
- private val incidentRepository: IncidentRepository
+ private val incidentRepository: IncidentRepository,
+ private val preferences: SharedPreferences
) : ViewModel() {
+ companion object {
+ private const val PREF_KEY_SHARE_CAPABILITY_REMINDER_SHOWN = "preference_key_share_incident_guide_shown"
+ }
+
internal val selectedState = ObservableField("")
private val _incidents = MediatorLiveData>()
val incidents: LiveData> = _incidents
+ private val _shareIncident = LiveEvent()
+ val shareIncident: LiveData = _shareIncident
+
+ private val _shouldShowShareCapabilityMessage = MutableLiveData()
+ val shouldShowShareCapabilityMessage: LiveData = _shouldShowShareCapabilityMessage
+
+ fun onShareIncidentClicked(incident: Incident) {
+ Timber.d("User clicked on share incident")
+ _shareIncident.value = incident
+ }
+
fun setArgs(navArgs: IncidentsFragmentArgs) {
navArgs.stateName?.let { selectedSate(it) }
if (navArgs.timestamp != 0L) {
selectedTimestamp(navArgs.timestamp)
}
+
+ val isMessageShown = preferences.getBoolean(PREF_KEY_SHARE_CAPABILITY_REMINDER_SHOWN, false)
+ if (isMessageShown.not()) {
+ Timber.d("User has launched app for first time, show share capability message.")
+ preferences.edit().putBoolean(PREF_KEY_SHARE_CAPABILITY_REMINDER_SHOWN, true).apply()
+ _shouldShowShareCapabilityMessage.value = Unit
+ } else {
+ Timber.d("Not first time app launch, don't show share capability message.")
+ }
}
private fun selectedSate(stateName: String) {
diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/incident/IncidentsFragment.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/incident/IncidentsFragment.kt
index c29fe7e..9e13f2e 100644
--- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/incident/IncidentsFragment.kt
+++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/incident/IncidentsFragment.kt
@@ -11,21 +11,34 @@ import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import com.blacklivesmatter.policebrutality.R
+import com.blacklivesmatter.policebrutality.analytics.Analytics
+import com.blacklivesmatter.policebrutality.analytics.Analytics.Companion.CONTENT_TYPE_INCIDENT_SHARE
+import com.blacklivesmatter.policebrutality.data.model.Incident
+import com.blacklivesmatter.policebrutality.databinding.DialogBottomsheetIncidentDetailsBinding
import com.blacklivesmatter.policebrutality.databinding.FragmentIncidentsBinding
+import com.blacklivesmatter.policebrutality.ui.extensions.observeKotlin
import com.blacklivesmatter.policebrutality.ui.util.IntentBuilder
+import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.snackbar.Snackbar
import dagger.android.support.DaggerFragment
import timber.log.Timber
import javax.inject.Inject
+/**
+ * Shows list of incidents that happened during the peaceful protest.
+ */
class IncidentsFragment : DaggerFragment() {
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
+ @Inject
+ lateinit var analytics: Analytics
+
private val viewModel by viewModels { viewModelFactory }
private lateinit var viewDataBinding: FragmentIncidentsBinding
private val navArgs: IncidentsFragmentArgs by navArgs()
private lateinit var adapter: IncidentsAdapter
+ private lateinit var bottomSheetShareDialog: BottomSheetDialog
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
viewDataBinding = FragmentIncidentsBinding.inflate(inflater, container, false).apply {
@@ -39,6 +52,12 @@ class IncidentsFragment : DaggerFragment() {
isDateBasedIncidents = navArgs.isDateBased(),
itemClickCallback = { clickedIncident ->
Timber.d("Selected Incident: $clickedIncident")
+ analytics.logSelectItem(
+ type = Analytics.CONTENT_TYPE_INCIDENT,
+ id = clickedIncident.id,
+ name = clickedIncident.incident_id ?: "---"
+ )
+ showIncidentDetailsForSharing(clickedIncident)
}, linkClickCallback = { clickedLink ->
openWebPage(clickedLink)
}
@@ -62,6 +81,48 @@ class IncidentsFragment : DaggerFragment() {
viewModel.incidents.observe(viewLifecycleOwner, Observer {
adapter.submitList(it)
})
+
+ viewModel.shareIncident.observeKotlin(viewLifecycleOwner) { incident ->
+ if (bottomSheetShareDialog.isShowing) {
+ bottomSheetShareDialog.dismiss()
+ }
+ analytics.logSelectItem(CONTENT_TYPE_INCIDENT_SHARE, incident.id, incident.incident_id ?: "---")
+ startActivity(IntentBuilder.share(incident))
+ }
+
+ viewModel.shouldShowShareCapabilityMessage.observeKotlin(viewLifecycleOwner) {
+ Snackbar.make(
+ viewDataBinding.root,
+ R.string.message_share_incident_capability,
+ Snackbar.LENGTH_INDEFINITE
+ ).setAction(R.string.button_cta_thanks, {}).show()
+ }
+ }
+
+ override fun onStart() {
+ super.onStart()
+ activity?.let {
+ analytics.logPageView(
+ it, if (navArgs.isDateBased()) Analytics.SCREEN_INCIDENT_LIST_BY_DATE
+ else Analytics.SCREEN_INCIDENT_LIST_BY_LOCATION
+ )
+ }
+ }
+
+ private fun showIncidentDetailsForSharing(incident: Incident) {
+ Timber.d("User tapped on the incident item. Show details and allow sharing.")
+ val context = context ?: return
+
+ val incidentBinding = DialogBottomsheetIncidentDetailsBinding.inflate(layoutInflater, null, false).apply {
+ lifecycleOwner = this@IncidentsFragment
+ data = incident // Allows in future to use data binding to provide more info in bottom sheet dialog
+ vm = viewModel
+ }
+
+ bottomSheetShareDialog = BottomSheetDialog(context)
+ bottomSheetShareDialog.setContentView(incidentBinding.root)
+ bottomSheetShareDialog.dismissWithAnimation = true
+ bottomSheetShareDialog.show()
}
/**
@@ -77,7 +138,7 @@ class IncidentsFragment : DaggerFragment() {
}
}
- private fun IncidentsFragmentArgs.isDateBased(): Boolean = navArgs.timestamp != 0L
+ private fun IncidentsFragmentArgs.isDateBased(): Boolean = timestamp != 0L
private fun IncidentsFragmentArgs.titleResId(): Int =
if (isDateBased()) R.string.title_incidents_on_date else R.string.title_incidents_at_location
diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/incidentlocations/LocationFragment.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/incidentlocations/LocationFragment.kt
index 20da5c5..f7daef7 100644
--- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/incidentlocations/LocationFragment.kt
+++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/incidentlocations/LocationFragment.kt
@@ -13,6 +13,7 @@ import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.blacklivesmatter.policebrutality.R
+import com.blacklivesmatter.policebrutality.analytics.Analytics
import com.blacklivesmatter.policebrutality.config.THE_846_DAY
import com.blacklivesmatter.policebrutality.databinding.FragmentIncidentLocationsBinding
import com.blacklivesmatter.policebrutality.ui.extensions.observeKotlin
@@ -37,6 +38,9 @@ class LocationFragment : DaggerFragment() {
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
+ @Inject
+ lateinit var analytics: Analytics
+
private val viewModel by viewModels { viewModelFactory }
private lateinit var viewDataBinding: FragmentIncidentLocationsBinding
private lateinit var adapter: LocationListAdapter
@@ -53,6 +57,7 @@ class LocationFragment : DaggerFragment() {
adapter = LocationListAdapter { state ->
Timber.d("Tapped on state item $state")
+ analytics.logSelectItem(Analytics.CONTENT_TYPE_LOCATION, state.stateName, state.stateName)
findNavController().navigate(
LocationFragmentDirections.navigationToIncidentsFragment(stateName = state.stateName)
)
@@ -107,6 +112,11 @@ class LocationFragment : DaggerFragment() {
setupSwipeRefreshAction()
}
+ override fun onStart() {
+ super.onStart()
+ activity?.let { analytics.logPageView(it, Analytics.SCREEN_INCIDENT_LOCATION) }
+ }
+
private fun setupSwipeRefreshAction() {
viewDataBinding.swipeRefresh.setColorSchemeResources(R.color.teal_700, R.color.blue_grey_500, R.color.teal_800)
viewDataBinding.swipeRefresh.setOnRefreshListener {
@@ -188,5 +198,6 @@ class LocationFragment : DaggerFragment() {
viewModel.onDateTimeStampSelected(viewLifecycleOwner, selectedTimeStamp)
}
picker.show(childFragmentManager, picker.toString())
+ activity?.let { analytics.logPageView(it, Analytics.SCREEN_INCIDENT_DATE_FILTER) }
}
}
diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/incidentlocations/LocationViewModel.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/incidentlocations/LocationViewModel.kt
index 9d07132..188127d 100644
--- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/incidentlocations/LocationViewModel.kt
+++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/incidentlocations/LocationViewModel.kt
@@ -7,6 +7,7 @@ import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import com.blacklivesmatter.policebrutality.analytics.Analytics
import com.blacklivesmatter.policebrutality.data.IncidentRepository
import com.blacklivesmatter.policebrutality.data.model.Incident
import com.blacklivesmatter.policebrutality.data.model.LocationIncidents
@@ -22,7 +23,8 @@ import java.util.concurrent.TimeUnit
import javax.inject.Inject
class LocationViewModel @Inject constructor(
- private val incidentRepository: IncidentRepository
+ private val incidentRepository: IncidentRepository,
+ private val analytics: Analytics
) : ViewModel() {
sealed class NavigationEvent {
data class Filter(val timestamp: Long, val dateText: String) : NavigationEvent()
@@ -75,6 +77,7 @@ class LocationViewModel @Inject constructor(
Timber.w("Already loading content. Ignore additional refresh request.")
return
}
+ analytics.logEvent(Analytics.ACTION_INCIDENT_REFRESH)
isOperationInProgress.set(true)
_refreshEvent.value = RefreshEvent.Loading
diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/moreinfo/MoreInfoFragment.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/moreinfo/MoreInfoFragment.kt
index dc3bcc1..f9110f6 100644
--- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/moreinfo/MoreInfoFragment.kt
+++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/moreinfo/MoreInfoFragment.kt
@@ -15,6 +15,7 @@ import androidx.core.view.children
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import com.blacklivesmatter.policebrutality.R
+import com.blacklivesmatter.policebrutality.analytics.Analytics
import com.blacklivesmatter.policebrutality.databinding.FragmentMoreInfoBinding
import com.blacklivesmatter.policebrutality.ui.extensions.observeKotlin
import com.blacklivesmatter.policebrutality.ui.util.IntentBuilder
@@ -30,6 +31,9 @@ class MoreInfoFragment : DaggerFragment() {
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
+ @Inject
+ lateinit var analytics: Analytics
+
private val viewModel by viewModels { viewModelFactory }
private lateinit var viewDataBinding: FragmentMoreInfoBinding
@@ -57,8 +61,14 @@ class MoreInfoFragment : DaggerFragment() {
handleExternalUrl()
}
+ override fun onStart() {
+ super.onStart()
+ activity?.let { analytics.logPageView(it, Analytics.SCREEN_MORE_INFO) }
+ }
+
private fun handleExternalUrl() {
viewModel.openExternalUrl.observeKotlin(viewLifecycleOwner) { url ->
+ analytics.logSelectItem(Analytics.CONTENT_TYPE_PB2020_LINK, url, url)
val intent = IntentBuilder.build(requireContext(), url)
if (intent != null) {
startActivity(intent)
@@ -86,6 +96,7 @@ class MoreInfoFragment : DaggerFragment() {
}
private fun copyTextToClipboard(copyText: String) {
+ analytics.logSelectItem(Analytics.CONTENT_TYPE_HASHTAG, copyText, copyText)
val clipboardManager: ClipboardManager =
ContextCompat.getSystemService(requireContext(), ClipboardManager::class.java)!!
val clipData = ClipData.newPlainText("text", copyText)
@@ -117,12 +128,14 @@ class MoreInfoFragment : DaggerFragment() {
Timber.d("Share app menu item selected.")
// TODO - update this whenever app is published at
// https://play.google.com/store/apps/details?id=com.blacklivesmatter.policebrutality
+ // TODO https://github.com/amardeshbd/android-police-brutality-incidents/issues/55
Snackbar.make(
viewDataBinding.root,
"Sharing coming soon: This app is pending approval on Google Play Store. " +
"Thanks for caring! ❤️",
Snackbar.LENGTH_LONG
).show()
+ analytics.logEvent(Analytics.ACTION_SHARE_APP)
return true
}
else -> {
@@ -136,5 +149,6 @@ class MoreInfoFragment : DaggerFragment() {
.setPositiveButton(R.string.button_cta_okay, null)
.setView(R.layout.dialog_about_app)
.show()
+ activity?.let { analytics.logPageView(it, Analytics.SCREEN_ABOUT_APP) }
}
}
diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/newreport/NewReportFragment.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/newreport/NewReportFragment.kt
index d0dbcb0..4955c05 100644
--- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/newreport/NewReportFragment.kt
+++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/newreport/NewReportFragment.kt
@@ -9,6 +9,7 @@ import androidx.core.text.HtmlCompat
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import com.blacklivesmatter.policebrutality.R
+import com.blacklivesmatter.policebrutality.analytics.Analytics
import com.blacklivesmatter.policebrutality.databinding.FragmentNewReportBinding
import com.blacklivesmatter.policebrutality.ui.extensions.observeKotlin
import com.blacklivesmatter.policebrutality.ui.util.IntentBuilder
@@ -21,6 +22,9 @@ class NewReportFragment : DaggerFragment() {
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
+ @Inject
+ lateinit var analytics: Analytics
+
private val viewModel by viewModels { viewModelFactory }
private lateinit var viewDataBinding: FragmentNewReportBinding
@@ -51,6 +55,11 @@ class NewReportFragment : DaggerFragment() {
}
}
+ override fun onStart() {
+ super.onStart()
+ activity?.let { analytics.logPageView(it, Analytics.SCREEN_REPORT_INCIDENT) }
+ }
+
private fun bindGuideText(textView: MaterialTextView) {
val message = HtmlCompat.fromHtml(
resources.getText(R.string.report_new_incident_guideline_text) as String,
diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/newreport/NewReportViewModel.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/newreport/NewReportViewModel.kt
index 57a2999..5e5ec1f 100644
--- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/newreport/NewReportViewModel.kt
+++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/newreport/NewReportViewModel.kt
@@ -2,15 +2,19 @@ package com.blacklivesmatter.policebrutality.ui.newreport
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
+import com.blacklivesmatter.policebrutality.analytics.Analytics
import com.blacklivesmatter.policebrutality.config.REPORT_INCIDENT_WEB_URL
import com.blacklivesmatter.policebrutality.ui.extensions.LiveEvent
import javax.inject.Inject
-class NewReportViewModel @Inject constructor() : ViewModel() {
+class NewReportViewModel @Inject constructor(
+ private val analytics: Analytics
+) : ViewModel() {
private val _openReportIncidentUrl = LiveEvent()
val openReportIncidentUrl: LiveData = _openReportIncidentUrl
fun onReportIncidentClicked() {
+ analytics.logEvent(Analytics.ACTION_INCIDENT_REPORT_NEW)
_openReportIncidentUrl.value = REPORT_INCIDENT_WEB_URL
}
}
diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/util/IntentBuilder.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/util/IntentBuilder.kt
index ca2c742..e66f9fd 100644
--- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/util/IntentBuilder.kt
+++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/util/IntentBuilder.kt
@@ -3,6 +3,8 @@ package com.blacklivesmatter.policebrutality.ui.util
import android.content.Context
import android.content.Intent
import android.net.Uri
+import com.blacklivesmatter.policebrutality.config.PB_LINK_WEB
+import com.blacklivesmatter.policebrutality.data.model.Incident
object IntentBuilder {
// Google Play Application IDs that allows to launch appropriate app without having chooser intent.
@@ -57,4 +59,35 @@ object IntentBuilder {
// Finally if nothing works, return null as error indication
return null
}
+
+ /**
+ * Builds intent to share [incident] via email or other social network
+ *
+ * See: https://developer.android.com/training/sharing/send#send-text-content
+ */
+ fun share(incident: Incident): Intent {
+ // First, build the share texts
+ val shareContentTitle = "Check this incident that was reported at $PB_LINK_WEB"
+
+ val shareBodyText = """
+$shareContentTitle
+
+- Incident: ${incident.name}
+- Location: ${incident.city}, ${incident.state}
+- Date: ${incident.date}
+
+Reference/Evidence Links:
+${incident.links.joinToString(separator = " \n * ", prefix = " * ")}
+ """.trimIndent()
+
+ val sendIntent: Intent = Intent().apply {
+ action = Intent.ACTION_SEND
+ putExtra(Intent.EXTRA_TEXT, shareBodyText)
+ putExtra(Intent.EXTRA_SUBJECT, shareContentTitle)
+ type = "text/plain"
+ }
+
+ val shareIntent = Intent.createChooser(sendIntent, null)
+ return shareIntent
+ }
}
diff --git a/android-app/app/src/main/res/layout/dialog_bottomsheet_incident_details.xml b/android-app/app/src/main/res/layout/dialog_bottomsheet_incident_details.xml
new file mode 100644
index 0000000..1d91543
--- /dev/null
+++ b/android-app/app/src/main/res/layout/dialog_bottomsheet_incident_details.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android-app/app/src/main/res/values/strings.xml b/android-app/app/src/main/res/values/strings.xml
index 2603a05..863ae1f 100644
--- a/android-app/app/src/main/res/values/strings.xml
+++ b/android-app/app/src/main/res/values/strings.xml
@@ -26,6 +26,8 @@
Sorry, there are no incidents reported on selected date %1$s
Successfully refreshed %1$s incidents from source \'2020PB/police-brutality\'.
Sorry, we are unable to refresh new incidents data at the moment. Please try again later.
+ Share this incident with %1$s external link(s)?
+ Did you know, you can share incident details by just tapping on it?
Justice for George Floyd protest fist image icon
@@ -38,6 +40,8 @@
About App
Credits
OK
+ Thanks
+ Share Incident
Total %d reported incidents.