Skip to content

Commit

Permalink
Merge pull request #84 from amardeshbd/develop
Browse files Browse the repository at this point in the history
Release 1.4
  • Loading branch information
hossain-khan authored Jun 11, 2020
2 parents 14a2001 + db2fd6c commit 80199eb
Show file tree
Hide file tree
Showing 13 changed files with 96 additions and 9 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,5 @@ lint/tmp/
.DS_Store
android-app/keystore.properties
android-app/app/release/output.json
android-app/keystore/upload-keystore.jks
android-app/keystore/private_key.pepk
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
> **NOTE:** _This repository was created on June 6th, 2020 after discovering the [police-brutality](https://github.com/2020PB/police-brutality) repository that contains the incidents data. The hope is to quickly build an Android app that is easily accessible for people to get updates on new incidents or browse existing incidents._
# Police Brutality Incidents - Android App

Android client app for https://github.com/2020PB/police-brutality (Repository containing evidence of police brutality during the 2020 George Floyd protests)
Expand Down Expand Up @@ -48,6 +46,6 @@ Currently following key features are being used.

### Preview

Here is a snapshot of the app in early stages _(taken on June 8th)_
Here is a snapshot of the app in early stages _(taken on June 10th)_

<img src="https://user-images.githubusercontent.com/99822/84097521-dc272280-a9d2-11ea-9022-3f57b45f7096.gif" width="50%">
<img src="https://user-images.githubusercontent.com/99822/84335138-28ed3380-ab62-11ea-9f10-0a61bfde1f44.gif" width="50%">
10 changes: 7 additions & 3 deletions android-app/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ android {
applicationId "com.blacklivesmatter.policebrutality"
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
versionCode 4
versionName "1.3"
versionCode 5
versionName "1.4"

resConfigs "en"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

Expand All @@ -30,6 +32,8 @@ android {
arguments += ["room.schemaLocation": "$projectDir/schemas".toString()]
}
}

resValue("string", "app_version_name", "v${versionName}")
}

signingConfigs {
Expand All @@ -42,7 +46,7 @@ android {
}


buildFeatures {
buildFeatures { // https://developer.android.com/studio/releases/gradle-plugin#buildFeatures
dataBinding = true
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ class BrutalityIncidentRepository @Inject constructor(
return incidentDao.getTotalIncidentsOnDate(timeStamp)
}

override fun getIncidentDates(): LiveData<List<String>> {
return incidentDao.getIncidentDates()
}

override suspend fun getIncidentsCoroutine(): List<Incident> {
val source = incidentApi.getAllIncidents()
return source.data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ interface IncidentDao {
@Query("SELECT COUNT(*) as count FROM incidents where DATE(date) = DATE(DATETIME(:timestamp, 'unixepoch'))")
fun getTotalIncidentsOnDate(timestamp: Long): LiveData<Int>

@Query("SELECT DATE(date) as date_text FROM incidents GROUP BY DATE(date) ORDER BY date_text")
fun getIncidentDates(): LiveData<List<String>>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(incidents: List<Incident>)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface IncidentRepository {
fun getIncidentsByDate(timeStamp: Long): LiveData<List<Incident>>
fun getLocations(): LiveData<List<LocationIncidents>>
fun getTotalIncidentsOnDate(timeStamp: Long): LiveData<Int>
fun getIncidentDates(): LiveData<List<String>>

/*
* Based on return type table using live data provides us with lifecycle aware stream of data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import com.blacklivesmatter.policebrutality.databinding.FragmentIncidentLocation
import com.blacklivesmatter.policebrutality.ui.extensions.observeKotlin
import com.blacklivesmatter.policebrutality.ui.incidentlocations.LocationViewModel.NavigationEvent
import com.blacklivesmatter.policebrutality.ui.incidentlocations.LocationViewModel.RefreshEvent
import com.blacklivesmatter.policebrutality.ui.util.IncidentAvailabilityValidator
import com.google.android.material.datepicker.CalendarConstraints
import com.google.android.material.datepicker.CalendarConstraints.DateValidator
import com.google.android.material.datepicker.CompositeDateValidator
Expand All @@ -39,6 +40,7 @@ class LocationFragment : DaggerFragment() {
private val viewModel by viewModels<LocationViewModel> { viewModelFactory }
private lateinit var viewDataBinding: FragmentIncidentLocationsBinding
private lateinit var adapter: LocationListAdapter
private var incidentDates: List<String> = emptyList()

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
viewDataBinding = FragmentIncidentLocationsBinding.inflate(inflater, container, false).apply {
Expand Down Expand Up @@ -96,6 +98,12 @@ class LocationFragment : DaggerFragment() {
}
}

viewModel.incidentDates.observeKotlin(viewLifecycleOwner) { incidentDates ->
// Saves the dates for use in date picker
Timber.d("Received list of incident dates: $incidentDates")
this.incidentDates = incidentDates
}

setupSwipeRefreshAction()
}

Expand Down Expand Up @@ -164,11 +172,13 @@ class LocationFragment : DaggerFragment() {
val validators: MutableList<DateValidator> = ArrayList()
validators.add(DateValidatorPointForward.from(the846Day.timeInMillis))
validators.add(DateValidatorPointBackward.now())
validators.add(IncidentAvailabilityValidator(incidentDates))

val builder = MaterialDatePicker.Builder.datePicker()
.setCalendarConstraints(
CalendarConstraints.Builder()
.setStart(the846Day.timeInMillis)
.setEnd(System.currentTimeMillis())
.setValidator(CompositeDateValidator.allOf(validators))
.build()
).setTitleText(R.string.title_filter_incidents_by_date)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class LocationViewModel @Inject constructor(
val isOperationInProgress = ObservableField(false)

val locations: LiveData<List<LocationIncidents>> = incidentRepository.getLocations()
val incidentDates: LiveData<List<String>> = incidentRepository.getIncidentDates()

private val _dateFilterMediatorEvent = MediatorLiveData<NavigationEvent>()
private val _dateFilterEvent = LiveEvent<NavigationEvent>()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.blacklivesmatter.policebrutality.ui.util

import android.os.Parcel
import android.os.Parcelable
import com.google.android.material.datepicker.CalendarConstraints
import org.threeten.bp.Instant
import org.threeten.bp.OffsetDateTime
import org.threeten.bp.ZoneId
import org.threeten.bp.format.DateTimeFormatter
import timber.log.Timber
import java.util.Locale

/**
* Validates if given date has incident records.
*/
class IncidentAvailabilityValidator(
/**
* List of date that has available incident records
*/
private val datesIncidentHappened: List<String>
) : CalendarConstraints.DateValidator {
private val dateFormat: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd", Locale.US)
override fun isValid(date: Long): Boolean {
if (datesIncidentHappened.isEmpty()) {
return true // Don't block all the dates in case we don't have dates data
}

val instant = Instant.ofEpochMilli(date)
val offsetDateTime: OffsetDateTime = OffsetDateTime.ofInstant(instant, ZoneId.of("UTC"))
val formattedDate = offsetDateTime.format(dateFormat)

val didIncidentHappenOnThisDay = datesIncidentHappened.contains(formattedDate)
Timber.d("Did incident happen on $formattedDate : $didIncidentHappenOnThisDay")
return didIncidentHappenOnThisDay
}

/** Part of [android.os.Parcelable] requirements. Do not use. */
override fun describeContents(): Int = 0
override fun writeToParcel(dest: Parcel?, flags: Int) {
dest?.writeList(datesIncidentHappened)
}

companion object CREATOR : Parcelable.Creator<IncidentAvailabilityValidator> {
override fun createFromParcel(parcel: Parcel): IncidentAvailabilityValidator {
return IncidentAvailabilityValidator(emptyList())
}

override fun newArray(size: Int): Array<IncidentAvailabilityValidator?> {
return arrayOfNulls(size)
}
}
}
10 changes: 10 additions & 0 deletions android-app/app/src/main/res/layout/dialog_about_app.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@
app:layout_constraintStart_toStartOf="@+id/guideline_start"
app:layout_constraintTop_toBottomOf="@id/guideline_top" />

<com.google.android.material.textview.MaterialTextView
style="?textAppearanceCaption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/view_spacing_medium"
android:text="@string/app_version_name"
app:layout_constraintBottom_toBottomOf="@+id/about_app_title"
app:layout_constraintStart_toEndOf="@+id/about_app_title"
app:layout_constraintTop_toTopOf="@+id/about_app_title" />

<com.google.android.material.textview.MaterialTextView
android:id="@+id/about_app_description"
style="?textAppearanceBody1"
Expand Down
4 changes: 2 additions & 2 deletions android-app/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,6 @@
first to find out the Incident Report Guidelines and Frequently Asked Questions (FAQ).</p>]]></string>

<string name="message_report_incident_external_link_info">NOTE: This opens link on your browser app. You will need GitHub account to report incident in the \'2020PB/police-brutality\' GitHub repository.</string>
<string name="info_message_about_app_background"><![CDATA[This app is developed to provide easy access to police brutality incidents that happened during protest for George Floyd\'s murder.\n\nThe app is open-source and available for contribution. If you find issue you can report it at https://bit.ly/PB2020App]]></string>
<string name="info_message_about_app_credits"><![CDATA[The app is actively maintained by following people:\n • Hossain Khan (hossain.dev)\n\nArt work used:\n • Fist Icon - https://commons.wikimedia.org/wiki/File:Fist.svg (License: Public)\n • Graffiti mural honoring George Floyd by munshots - https://unsplash.com/photos/_vAC0je-hKo (License: Public)]]></string>
<string name="info_message_about_app_background"><![CDATA[This app is developed to provide easy access to police brutality incidents that happened during protest for George Floyd\'s murder.\n\nThe app is open-source and available for contribution. If you find issue you can report it at bit.ly/PB2020App]]></string>
<string name="info_message_about_app_credits"><![CDATA[The app is actively maintained by following people:\n • Hossain Khan (hossain.dev)\n\nArt work used:\n • Fist Icon - bit.ly/2AnXZd6 (License: Public)\n • Graffiti mural honoring George Floyd by munshots - bit.ly/3dRj524 (License: Public)\n • Font Awesome Icons (License: CC BY 4.0)]]></string>
</resources>
2 changes: 2 additions & 0 deletions resources/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ Poster source (credit): https://www.reddit.com/r/PlexPosters/comments/gx54fr/cou
### Additional External Sources
* https://commons.wikimedia.org/wiki/File:Fist_.svg
* https://commons.wikimedia.org/wiki/File:Fist.svg
* https://unsplash.com/photos/_vAC0je-hKo
* https://github.com/FortAwesome/Font-Awesome
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 80199eb

Please sign in to comment.