diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index a3e9516..be5cbaf 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -33,12 +33,8 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - - - name: Kotlin lint (use `./gradlew ktlintFormat` on error) - run: ./gradlew ktlintCheck - - name: Build with Gradle - run: ./gradlew build + run: ./gradlew assembleDebug - name: Archive APK uses: actions/upload-artifact@v1 diff --git a/.github/workflows/android_lint.yml b/.github/workflows/android_lint.yml new file mode 100644 index 0000000..5bc7e18 --- /dev/null +++ b/.github/workflows/android_lint.yml @@ -0,0 +1,38 @@ +name: Android Lint + +on: + push: + branches: [ develop ] + pull_request: + branches: [ develop ] + +jobs: + build: + + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./android-app + + steps: + - uses: actions/checkout@v2 + - name: set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + + # Save gradle cache between runs, see https://github.com/actions/cache/blob/master/examples.md#java---gradle + # Include hashFiles so that cache does continue to grow with old dependencies in it. + - name: Copy gradle cache + uses: actions/cache@v2 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Kotlin lint (use `./gradlew ktlintFormat` on error) + run: ./gradlew ktlintCheck + + - name: Android Lint + run: ./gradlew lint diff --git a/README.md b/README.md index 7ee5832..9956df2 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,6 @@ Currently following key features are being used. ### Preview -Here is a snapshot of the app in early stages _(taken on June 10th)_ +Here is a snapshot of the app in early stages _(taken on June 14th)_ - + diff --git a/android-app/.editorconfig b/android-app/.editorconfig index 2b1c2b1..7c12460 100644 --- a/android-app/.editorconfig +++ b/android-app/.editorconfig @@ -1,6 +1,9 @@ [*.{kt, kts}] # Disable Ktlint import ordering temporarily because of AndroidStudio Kotlin formatting bug. read more here: # https://youtrack.jetbrains.com/issue/KT-10974 & https://github.com/pinterest/ktlint/issues/527 -disabled_rules=import-ordering -kotlin_imports_layout=idea # This is not working at the moment (may required ktlint plugin update) -max_line_length=120 +disabled_rules = import-ordering +kotlin_imports_layout = idea # This is not working at the moment (may required ktlint plugin update) +max_line_length = 120 + +[*.xml] +max_line_length = 120 \ No newline at end of file diff --git a/android-app/.idea/dictionaries/hossain.xml b/android-app/.idea/dictionaries/hossain.xml index 61ca0ea..3c5fc33 100644 --- a/android-app/.idea/dictionaries/hossain.xml +++ b/android-app/.idea/dictionaries/hossain.xml @@ -1,6 +1,7 @@ + crashlytics hashtag diff --git a/android-app/app/build.gradle b/android-app/app/build.gradle index 6779f8e..682cb9e 100644 --- a/android-app/app/build.gradle +++ b/android-app/app/build.gradle @@ -6,6 +6,7 @@ plugins { id 'kotlin-android-extensions' id 'kotlin-kapt' id 'androidx.navigation.safeargs.kotlin' + id 'dagger.hilt.android.plugin' id 'com.google.gms.google-services' id 'com.google.firebase.crashlytics' id 'org.jlleitschuh.gradle.ktlint' @@ -20,8 +21,8 @@ android { applicationId "com.blacklivesmatter.policebrutality" minSdkVersion rootProject.minSdkVersion targetSdkVersion rootProject.targetSdkVersion - versionCode 6 - versionName "1.5" + versionCode 7 + versionName "2.0" resConfigs "en" @@ -69,6 +70,12 @@ android { kotlinOptions { jvmTarget = '1.8' } + + lintOptions { // https://developer.android.com/studio/write/lint.html + checkAllWarnings true + warningsAsErrors true + ignore 'GradleDependency', 'NewerVersionAvailable', 'UnusedIds' + } } dependencies { @@ -84,7 +91,6 @@ dependencies { implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$rootProject.lifecycleVersion" implementation "androidx.navigation:navigation-fragment-ktx:$rootProject.navigationVersion" implementation "androidx.navigation:navigation-ui-ktx:$rootProject.navigationVersion" - implementation "androidx.constraintlayout:constraintlayout:$rootProject.constraintLayoutVersion" implementation "androidx.cardview:cardview:$rootProject.cardViewVersion" implementation "androidx.preference:preference:$rootProject.preferenceVersion" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-rc01" @@ -93,24 +99,27 @@ dependencies { implementation "com.google.firebase:firebase-analytics-ktx:$rootProject.firebaseAnalyticsVersion" implementation "com.google.firebase:firebase-crashlytics:$rootProject.firebaseCrashlyticsVersion" + // Work Manager implementation "androidx.work:work-runtime-ktx:$rootProject.workVersion" + // Room Database implementation "androidx.room:room-runtime:$rootProject.roomVersion" implementation "androidx.room:room-ktx:$rootProject.roomVersion" kapt "androidx.room:room-compiler:$rootProject.roomVersion" - + // Glide - image loading kapt "com.github.bumptech.glide:compiler:$rootProject.glideVersion" implementation "com.github.bumptech.glide:glide:$rootProject.glideVersion" - implementation "com.google.code.gson:gson:$rootProject.gsonVersion" + // Google Gson for JSON + implementation "com.google.code.gson:gson:2.8.6" - // Dagger 2 - DI - implementation "com.google.dagger:dagger:$rootProject.dagger2Version" - implementation "com.google.dagger:dagger-android:$rootProject.dagger2Version" - implementation "com.google.dagger:dagger-android-support:$rootProject.dagger2Version" - kapt "com.google.dagger:dagger-android-processor:$rootProject.dagger2Version" - kapt "com.google.dagger:dagger-compiler:$rootProject.dagger2Version" + // Dagger Hilt (android dependency injection) + implementation "com.google.dagger:hilt-android:$hiltVersion" + kapt "com.google.dagger:hilt-android-compiler:$hiltVersion" + implementation "androidx.hilt:hilt-lifecycle-viewmodel:$rootProject.androidXHiltVersion" + implementation "androidx.hilt:hilt-work:$rootProject.androidXHiltVersion" + kapt "androidx.hilt:hilt-compiler:$rootProject.androidXHiltVersion" // Retrofit implementation "com.squareup.retrofit2:retrofit:$rootProject.retrofitVersion" @@ -119,12 +128,14 @@ dependencies { implementation "com.squareup.okhttp3:okhttp:$rootProject.okhttpVersion" implementation "com.squareup.okhttp3:logging-interceptor:$rootProject.okhttpVersion" - implementation "com.jakewharton.timber:timber:$rootProject.timberVersion" + // Timber for logging + implementation "com.jakewharton.timber:timber:4.7.1" + // 310BP - Backport of the Java SE 8 date-time classes implementation "org.threeten:threetenbp:1.4.4" // https://www.threeten.org/threetenbp/ // debugImplementation because LeakCanary should only run in debug builds. - debugImplementation "com.squareup.leakcanary:leakcanary-android:2.3" + debugImplementation "com.squareup.leakcanary:leakcanary-android:2.4" // Unit testing testImplementation "junit:junit:$rootProject.junitVersion" diff --git a/android-app/app/src/main/AndroidManifest.xml b/android-app/app/src/main/AndroidManifest.xml index d469c21..97389f2 100644 --- a/android-app/app/src/main/AndroidManifest.xml +++ b/android-app/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ @@ -11,13 +12,15 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:theme="@style/Theme.PoliceBrutality"> + android:theme="@style/Theme.PoliceBrutality" + tools:ignore="GoogleAppIndexingWarning"> + android:windowSoftInputMode="adjustResize" + tools:ignore="LockedOrientationActivity"> @@ -31,6 +34,16 @@ + + + \ No newline at end of file diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/BrutalityIncidentApplication.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/BrutalityIncidentApplication.kt index f2504c6..4e6f1a8 100644 --- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/BrutalityIncidentApplication.kt +++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/BrutalityIncidentApplication.kt @@ -1,25 +1,20 @@ package com.blacklivesmatter.policebrutality +import android.app.Application import androidx.appcompat.app.AppCompatDelegate -import com.blacklivesmatter.policebrutality.di.component.AppComponent -import com.blacklivesmatter.policebrutality.di.component.DaggerAppComponent +import androidx.hilt.work.HiltWorkerFactory +import androidx.work.Configuration +import com.blacklivesmatter.policebrutality.logging.TimberDebugTree +import com.blacklivesmatter.policebrutality.logging.TimberReleaseTree import com.blacklivesmatter.policebrutality.ui.common.ThemeHelper -import dagger.android.AndroidInjector -import dagger.android.support.DaggerApplication -import kotlin.math.abs +import dagger.hilt.android.HiltAndroidApp import timber.log.Timber +import javax.inject.Inject -class BrutalityIncidentApplication : DaggerApplication() { - override fun applicationInjector(): AndroidInjector { - // Build app component - val appComponent: AppComponent = DaggerAppComponent.builder() - .application(this) - .build() - - // Inject the application instance - appComponent.inject(this) - return appComponent - } +@HiltAndroidApp +class BrutalityIncidentApplication : Application(), Configuration.Provider { + @Inject + lateinit var workerFactory: HiltWorkerFactory override fun onCreate() { super.onCreate() @@ -28,30 +23,20 @@ class BrutalityIncidentApplication : DaggerApplication() { // Because it's #BlackLivesMatter ThemeHelper.applyTheme(AppCompatDelegate.MODE_NIGHT_YES) - if (BuildConfig.DEBUG) { - installLogging() - } + installLogging() } - private fun installLogging() { - // Include logging in release builds so we can test from client. - // See https://github.com/JakeWharton/timber/blob/master/timber-sample/src/main/java/com/example/timber/ExampleApp.java - Timber.plant(object : Timber.DebugTree() { - override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { - val newMessage = "[${getThreadName()}] $message" - super.log(priority, tag, newMessage, t) - } + override fun getWorkManagerConfiguration(): Configuration { + return Configuration.Builder() + .setWorkerFactory(workerFactory) + .build() + } - /** - * Returns name of current thread, with some well known thread names replaced to make easier to understand. - */ - private fun getThreadName(): String { - var name = Thread.currentThread().name - if (name.length > 10) { - name = name.substring(0, 10) + "." + abs(name.hashCode() % 100) - } - return name - } - }) + private fun installLogging() { + if (BuildConfig.DEBUG) { + Timber.plant(TimberDebugTree()) + } else { + Timber.plant(TimberReleaseTree()) + } } } diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/MainActivity.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/MainActivity.kt index 8dc1a22..da62936 100644 --- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/MainActivity.kt +++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/MainActivity.kt @@ -1,31 +1,27 @@ package com.blacklivesmatter.policebrutality import android.os.Bundle +import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil -import androidx.lifecycle.ViewModelProvider import androidx.navigation.findNavController import androidx.navigation.ui.setupWithNavController import com.blacklivesmatter.policebrutality.databinding.ActivityMainBinding import com.google.android.material.bottomnavigation.BottomNavigationView -import dagger.android.support.DaggerAppCompatActivity -import javax.inject.Inject +import dagger.hilt.android.AndroidEntryPoint /** * The container activity for the app using "single-activity" pattern. * This activity hosts fragments and the flow is managed by AndroidX Jetpack navigation library. */ -class MainActivity : DaggerAppCompatActivity() { - - @Inject - lateinit var viewModelFactory: ViewModelProvider.Factory - - private lateinit var viewModel: MainViewModel +@AndroidEntryPoint +class MainActivity : AppCompatActivity() { + private val viewModel: MainViewModel by viewModels() private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.activity_main) - viewModel = ViewModelProvider(this, viewModelFactory).get(MainViewModel::class.java) val navView: BottomNavigationView = findViewById(R.id.nav_view) diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/data/AppDatabase.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/data/AppDatabase.kt index 834fd3b..1136e25 100644 --- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/data/AppDatabase.kt +++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/data/AppDatabase.kt @@ -1,16 +1,9 @@ package com.blacklivesmatter.policebrutality.data -import android.content.Context import androidx.room.Database -import androidx.room.Room import androidx.room.RoomDatabase import androidx.room.TypeConverters -import androidx.sqlite.db.SupportSQLiteDatabase -import androidx.work.OneTimeWorkRequestBuilder -import androidx.work.WorkManager -import com.blacklivesmatter.policebrutality.config.DATABASE_NAME import com.blacklivesmatter.policebrutality.data.model.Incident -import com.blacklivesmatter.policebrutality.worker.SeedDatabaseWorker /** * The Room database for this app @@ -19,44 +12,4 @@ import com.blacklivesmatter.policebrutality.worker.SeedDatabaseWorker @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { abstract fun incidentDao(): IncidentDao - - companion object { - - // For Singleton instantiation - @Volatile - private var instance: AppDatabase? = null - - fun getInstance(context: Context): AppDatabase { - return instance ?: synchronized(this) { - instance ?: buildDatabase(context).also { instance = it } - } - } - - private fun buildDatabase(context: Context): AppDatabase { - return Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME) - .addCallback(object : RoomDatabase.Callback() { - override fun onCreate(db: SupportSQLiteDatabase) { - super.onCreate(db) - populateDatabase(context) - } - - override fun onDestructiveMigration(db: SupportSQLiteDatabase) { - super.onDestructiveMigration(db) - populateDatabase(context) - } - }) - // https://developer.android.com/training/data-storage/room/migrating-db-versions#kotlin - .fallbackToDestructiveMigration() - .build() - } - - /** - * Create and pre-populate the database. See this article for more details: - * https://medium.com/google-developers/7-pro-tips-for-room-fbadea4bfbd1#4785 - */ - private fun populateDatabase(context: Context) { - val request = OneTimeWorkRequestBuilder().build() - WorkManager.getInstance(context).enqueue(request) - } - } } diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/data/OffsetDateTimeConverter.java b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/data/OffsetDateTimeConverter.java index 2f08a93..2c4bdb5 100644 --- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/data/OffsetDateTimeConverter.java +++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/data/OffsetDateTimeConverter.java @@ -23,6 +23,8 @@ * THE SOFTWARE. */ +import androidx.annotation.NonNull; + import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; @@ -60,7 +62,8 @@ public class OffsetDateTimeConverter implements JsonSerializer, * @return a JsonElement corresponding to the specified object. */ @Override - public JsonElement serialize(final OffsetDateTime src, final Type typeOfSrc, final JsonSerializationContext context) { + @NonNull + public JsonElement serialize(@NonNull final OffsetDateTime src, @NonNull final Type typeOfSrc, @NonNull final JsonSerializationContext context) { return new JsonPrimitive(FORMATTER.format(src)); } @@ -80,7 +83,8 @@ public JsonElement serialize(final OffsetDateTime src, final Type typeOfSrc, fin * @throws JsonParseException if json is not in the expected format of {@code typeOfT} */ @Override - public OffsetDateTime deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) + @NonNull + public OffsetDateTime deserialize(@NonNull final JsonElement json, @NonNull final Type typeOfT, @NonNull final JsonDeserializationContext context) throws JsonParseException { return FORMATTER.parse(json.getAsString(), OffsetDateTime.FROM); } 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/AnalyticsModule.kt similarity index 65% rename from android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/AnalyticsModule.kt rename to android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/AnalyticsModule.kt index 3afabdf..970e4f2 100644 --- 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/AnalyticsModule.kt @@ -1,4 +1,4 @@ -package com.blacklivesmatter.policebrutality.di.module +package com.blacklivesmatter.policebrutality.di import android.content.Context import com.blacklivesmatter.policebrutality.analytics.Analytics @@ -6,16 +6,20 @@ import com.blacklivesmatter.policebrutality.analytics.AppAnalytics import com.google.firebase.analytics.FirebaseAnalytics import dagger.Module import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ApplicationComponent +import dagger.hilt.android.qualifiers.ApplicationContext import timber.log.Timber import javax.inject.Singleton +@InstallIn(ApplicationComponent::class) @Module class AnalyticsModule { @Singleton @Provides - fun provideAnalytics(context: Context): FirebaseAnalytics { + fun provideAnalytics(@ApplicationContext appContext: Context): FirebaseAnalytics { // https://firebase.google.com/docs/analytics/get-started?platform=android - val instance = FirebaseAnalytics.getInstance(context) + val instance = FirebaseAnalytics.getInstance(appContext) Timber.d("Providing firebase analytics instance: $instance") return instance } diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/ApiModule.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/ApiModule.kt similarity index 93% rename from android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/ApiModule.kt rename to android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/ApiModule.kt index 4d78121..85d081c 100644 --- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/ApiModule.kt +++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/ApiModule.kt @@ -1,4 +1,4 @@ -package com.blacklivesmatter.policebrutality.di.module +package com.blacklivesmatter.policebrutality.di import com.blacklivesmatter.policebrutality.BuildConfig import com.blacklivesmatter.policebrutality.api.IncidentApi @@ -7,6 +7,8 @@ import com.google.gson.Gson import com.google.gson.GsonBuilder import dagger.Module import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ApplicationComponent import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import org.threeten.bp.OffsetDateTime @@ -16,6 +18,7 @@ import retrofit2.converter.gson.GsonConverterFactory import java.util.concurrent.TimeUnit import javax.inject.Singleton +@InstallIn(ApplicationComponent::class) @Module class ApiModule { companion object { diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/DaoModule.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/DaoModule.kt new file mode 100644 index 0000000..78bdc76 --- /dev/null +++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/DaoModule.kt @@ -0,0 +1,65 @@ +package com.blacklivesmatter.policebrutality.di + +import android.content.Context +import android.content.SharedPreferences +import androidx.preference.PreferenceManager +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.sqlite.db.SupportSQLiteDatabase +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkManager +import com.blacklivesmatter.policebrutality.config.DATABASE_NAME +import com.blacklivesmatter.policebrutality.data.AppDatabase +import com.blacklivesmatter.policebrutality.data.IncidentDao +import com.blacklivesmatter.policebrutality.worker.SeedDatabaseWorker +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ApplicationComponent +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Singleton + +@InstallIn(ApplicationComponent::class) +@Module +object DaoModule { + + @Provides + @Singleton + fun provideDatabase(@ApplicationContext appContext: Context): AppDatabase { + /** + * Internal local function to create and pre-populate the database. + * See this article for more details: + * https://medium.com/google-developers/7-pro-tips-for-room-fbadea4bfbd1#4785 + */ + fun populateDatabase(context: Context) { + val request = OneTimeWorkRequestBuilder().build() + WorkManager.getInstance(context).enqueue(request) + } + + return Room.databaseBuilder(appContext, AppDatabase::class.java, DATABASE_NAME) + .addCallback(object : RoomDatabase.Callback() { + override fun onCreate(db: SupportSQLiteDatabase) { + super.onCreate(db) + populateDatabase(appContext) + } + + override fun onDestructiveMigration(db: SupportSQLiteDatabase) { + super.onDestructiveMigration(db) + populateDatabase(appContext) + } + }) + // https://developer.android.com/training/data-storage/room/migrating-db-versions#kotlin + .fallbackToDestructiveMigration() + .build() + } + + @Provides + fun provideIncidentDao(database: AppDatabase): IncidentDao { + return database.incidentDao() + } + + @Provides + fun provideSharedPreferences(@ApplicationContext appContext: Context): SharedPreferences { + return PreferenceManager.getDefaultSharedPreferences(appContext) + } +} diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/RepositoryModule.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/RepositoryModule.kt similarity index 66% rename from android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/RepositoryModule.kt rename to android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/RepositoryModule.kt index 8f08081..4e62cf7 100644 --- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/RepositoryModule.kt +++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/RepositoryModule.kt @@ -1,10 +1,13 @@ -package com.blacklivesmatter.policebrutality.di.module +package com.blacklivesmatter.policebrutality.di import com.blacklivesmatter.policebrutality.data.BrutalityIncidentRepository import com.blacklivesmatter.policebrutality.data.IncidentRepository import dagger.Binds import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ApplicationComponent +@InstallIn(ApplicationComponent::class) @Module abstract class RepositoryModule { diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/annotation/ViewModelKey.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/annotation/ViewModelKey.kt deleted file mode 100644 index 877f333..0000000 --- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/annotation/ViewModelKey.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.blacklivesmatter.policebrutality.di.annotation - -import androidx.lifecycle.ViewModel -import dagger.MapKey -import kotlin.reflect.KClass - -/** - * Used to add keys to view model factory. - */ -@Target( - AnnotationTarget.FUNCTION, - AnnotationTarget.PROPERTY_GETTER, - AnnotationTarget.PROPERTY_SETTER -) -@Retention(AnnotationRetention.RUNTIME) -@MapKey -annotation class ViewModelKey(val value: KClass) 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 deleted file mode 100644 index ae30165..0000000 --- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/component/AppComponent.kt +++ /dev/null @@ -1,44 +0,0 @@ -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 -import com.blacklivesmatter.policebrutality.di.module.RepositoryModule -import com.blacklivesmatter.policebrutality.di.module.ViewModelModule -import com.blacklivesmatter.policebrutality.di.module.WorkerModule -import dagger.BindsInstance -import dagger.Component -import dagger.android.AndroidInjector -import dagger.android.support.AndroidSupportInjectionModule -import javax.inject.Singleton - -@Singleton -@Component( - modules = [ - AndroidSupportInjectionModule::class, - ActivityModule::class, - AnalyticsModule::class, - ApiModule::class, - AppModule::class, - DaoModule::class, - RepositoryModule::class, - ViewModelModule::class, - WorkerModule::class - ] -) -interface AppComponent : AndroidInjector { - - @Component.Builder - interface Builder { - @BindsInstance - fun application(application: Application): Builder - - fun build(): AppComponent - } - - override fun inject(instance: BrutalityIncidentApplication?) -} diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/ActivityModule.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/ActivityModule.kt deleted file mode 100644 index 831b060..0000000 --- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/ActivityModule.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.blacklivesmatter.policebrutality.di.module - -import com.blacklivesmatter.policebrutality.MainActivity -import com.blacklivesmatter.policebrutality.ui.launcher.LauncherActivity -import dagger.Module -import dagger.android.AndroidInjectionModule -import dagger.android.ContributesAndroidInjector - -@Module(includes = [AndroidInjectionModule::class, FragmentBindingModule::class]) -abstract class ActivityModule { - - @ContributesAndroidInjector - abstract fun bindLauncherActivity(): LauncherActivity - - @ContributesAndroidInjector - abstract fun bindMainActivity(): MainActivity -} diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/AppModule.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/AppModule.kt deleted file mode 100644 index 8cccd4c..0000000 --- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/AppModule.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.blacklivesmatter.policebrutality.di.module - -import android.app.Application -import android.content.Context -import androidx.lifecycle.ViewModelProvider -import com.blacklivesmatter.policebrutality.vm.ViewModelFactory -import dagger.Binds -import dagger.Module - -@Module -abstract class AppModule { - - @Binds - abstract fun bindContext(application: Application): Context - - @Binds - abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory -} diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/DaoModule.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/DaoModule.kt deleted file mode 100644 index 229b56d..0000000 --- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/DaoModule.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.blacklivesmatter.policebrutality.di.module - -import android.content.Context -import android.content.SharedPreferences -import androidx.preference.PreferenceManager -import com.blacklivesmatter.policebrutality.data.AppDatabase -import com.blacklivesmatter.policebrutality.data.IncidentDao -import dagger.Module -import dagger.Provides - -@Module -class DaoModule { - - @Provides - fun provideIncidentDao(context: Context): IncidentDao { - return AppDatabase.getInstance(context).incidentDao() - } - - @Provides - fun provideSharedPreferences(context: Context): SharedPreferences { - return PreferenceManager.getDefaultSharedPreferences(context) - } -} diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/FragmentBindingModule.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/FragmentBindingModule.kt deleted file mode 100644 index 855278c..0000000 --- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/FragmentBindingModule.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.blacklivesmatter.policebrutality.di.module - -import com.blacklivesmatter.policebrutality.ui.incident.IncidentsFragment -import com.blacklivesmatter.policebrutality.ui.incidentlocations.LocationFragment -import com.blacklivesmatter.policebrutality.ui.moreinfo.MoreInfoFragment -import com.blacklivesmatter.policebrutality.ui.newreport.NewReportFragment -import dagger.Module -import dagger.android.ContributesAndroidInjector - -@Suppress("unused") -@Module -abstract class FragmentBindingModule { - @ContributesAndroidInjector - abstract fun contributeDashboardFragment(): NewReportFragment - - @ContributesAndroidInjector - abstract fun contributeHomeFragment(): LocationFragment - - @ContributesAndroidInjector - abstract fun contributeIncidentsFragment(): IncidentsFragment - - @ContributesAndroidInjector - abstract fun contributeNotificationsFragment(): MoreInfoFragment -} diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/ViewModelModule.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/ViewModelModule.kt deleted file mode 100644 index 37837e5..0000000 --- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/ViewModelModule.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.blacklivesmatter.policebrutality.di.module - -import androidx.lifecycle.ViewModel -import com.blacklivesmatter.policebrutality.MainViewModel -import com.blacklivesmatter.policebrutality.di.annotation.ViewModelKey -import com.blacklivesmatter.policebrutality.ui.incident.IncidentViewModel -import com.blacklivesmatter.policebrutality.ui.incidentlocations.LocationViewModel -import com.blacklivesmatter.policebrutality.ui.launcher.LauncherViewModel -import com.blacklivesmatter.policebrutality.ui.moreinfo.MoreInfoViewModel -import com.blacklivesmatter.policebrutality.ui.newreport.NewReportViewModel -import dagger.Binds -import dagger.Module -import dagger.multibindings.IntoMap - -@Module -abstract class ViewModelModule { - @Binds - @IntoMap - @ViewModelKey(LauncherViewModel::class) - abstract fun bindLauncherViewModel(viewModel: LauncherViewModel): ViewModel - - @Binds - @IntoMap - @ViewModelKey(MainViewModel::class) - abstract fun bindMainViewModel(viewModel: MainViewModel): ViewModel - - @Binds - @IntoMap - @ViewModelKey(NewReportViewModel::class) - abstract fun bindDashboardViewModel(viewModel: NewReportViewModel): ViewModel - - @Binds - @IntoMap - @ViewModelKey(LocationViewModel::class) - abstract fun bindHomeViewModel(viewModel: LocationViewModel): ViewModel - - @Binds - @IntoMap - @ViewModelKey(IncidentViewModel::class) - abstract fun bindIncidentViewModel(viewModel: IncidentViewModel): ViewModel - - @Binds - @IntoMap - @ViewModelKey(MoreInfoViewModel::class) - abstract fun bindNotificationsViewModel(viewModel: MoreInfoViewModel): ViewModel -} diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/WorkerModule.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/WorkerModule.kt deleted file mode 100644 index e4f6d9c..0000000 --- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/di/module/WorkerModule.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.blacklivesmatter.policebrutality.di.module - -import com.blacklivesmatter.policebrutality.worker.SeedDatabaseWorker -import dagger.Module -import dagger.android.AndroidInjectionModule -import dagger.android.ContributesAndroidInjector - -/** - * Module to bind Work Manager workers. - * - * See [Inject into Workers (androidx.WorkManager API)](https://github.com/google/dagger/issues/1183#issuecomment-601158396) - */ -@Module(includes = [AndroidInjectionModule::class]) -abstract class WorkerModule { - @ContributesAndroidInjector - abstract fun bindSeedDatabaseWorker(): SeedDatabaseWorker -} diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/logging/TimberDebugTree.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/logging/TimberDebugTree.kt new file mode 100644 index 0000000..bdf50dc --- /dev/null +++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/logging/TimberDebugTree.kt @@ -0,0 +1,26 @@ +package com.blacklivesmatter.policebrutality.logging + +import timber.log.Timber +import kotlin.math.abs + +/** + * DEBUG Logging tree for timber. + * See https://github.com/JakeWharton/timber/blob/master/timber-sample/src/main/java/com/example/timber/ExampleApp.java + */ +class TimberDebugTree : Timber.DebugTree() { + override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { + val newMessage = "[${getThreadName()}] $message" + super.log(priority, tag, newMessage, t) + } + + /** + * Returns name of current thread, with some well known thread names replaced to make easier to understand. + */ + private fun getThreadName(): String { + var name = Thread.currentThread().name + if (name.length > 10) { + name = name.substring(0, 10) + "." + abs(name.hashCode() % 100) + } + return name + } +} diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/logging/TimberReleaseTree.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/logging/TimberReleaseTree.kt new file mode 100644 index 0000000..64f91e7 --- /dev/null +++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/logging/TimberReleaseTree.kt @@ -0,0 +1,21 @@ +package com.blacklivesmatter.policebrutality.logging + +import android.util.Log +import com.google.firebase.crashlytics.FirebaseCrashlytics +import timber.log.Timber + +/** + * RELEASE logging tree that logs to [FirebaseCrashlytics]. + */ +class TimberReleaseTree : Timber.Tree() { + override fun log(priority: Int, tag: String?, message: String, throwable: Throwable?) { + if (priority == Log.VERBOSE) return + + FirebaseCrashlytics.getInstance().log(message) + if (throwable != null) { + // Notifies firebase about non fatal crashes + // See "Configure email alerts" https://support.google.com/firebase/answer/7276542?hl=en + FirebaseCrashlytics.getInstance().recordException(throwable) + } + } +} 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 8a97508..537ae8e 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 @@ -2,6 +2,7 @@ package com.blacklivesmatter.policebrutality.ui.incident import android.content.SharedPreferences import androidx.databinding.ObservableField +import androidx.hilt.lifecycle.ViewModelInject import androidx.lifecycle.LiveData import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData @@ -10,9 +11,8 @@ 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( +class IncidentViewModel @ViewModelInject constructor( private val incidentRepository: IncidentRepository, private val preferences: SharedPreferences ) : ViewModel() { 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 9e13f2e..16e131d 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 @@ -4,9 +4,9 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProvider import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager @@ -20,21 +20,19 @@ 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 dagger.hilt.android.AndroidEntryPoint 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 - +@AndroidEntryPoint +class IncidentsFragment : Fragment() { @Inject lateinit var analytics: Analytics - private val viewModel by viewModels { viewModelFactory } + private val viewModel by viewModels() private lateinit var viewDataBinding: FragmentIncidentsBinding private val navArgs: IncidentsFragmentArgs by navArgs() private lateinit var adapter: IncidentsAdapter 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 f7daef7..bdd3c1e 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 @@ -8,8 +8,8 @@ import android.view.MenuItem import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels -import androidx.lifecycle.ViewModelProvider import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager import com.blacklivesmatter.policebrutality.R @@ -27,21 +27,19 @@ import com.google.android.material.datepicker.DateValidatorPointBackward import com.google.android.material.datepicker.DateValidatorPointForward import com.google.android.material.datepicker.MaterialDatePicker import com.google.android.material.snackbar.Snackbar -import dagger.android.support.DaggerFragment +import dagger.hilt.android.AndroidEntryPoint import org.threeten.bp.DateTimeUtils import timber.log.Timber import java.util.ArrayList import java.util.Calendar import javax.inject.Inject -class LocationFragment : DaggerFragment() { - @Inject - lateinit var viewModelFactory: ViewModelProvider.Factory - +@AndroidEntryPoint +class LocationFragment : Fragment() { @Inject lateinit var analytics: Analytics - private val viewModel by viewModels { viewModelFactory } + private val viewModel by viewModels() private lateinit var viewDataBinding: FragmentIncidentLocationsBinding private lateinit var adapter: LocationListAdapter private var incidentDates: List = emptyList() 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 6a60fb3..c44a6f2 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 @@ -1,6 +1,7 @@ package com.blacklivesmatter.policebrutality.ui.incidentlocations import androidx.databinding.ObservableField +import androidx.hilt.lifecycle.ViewModelInject import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LiveData import androidx.lifecycle.MediatorLiveData @@ -12,7 +13,6 @@ import com.blacklivesmatter.policebrutality.data.IncidentRepository import com.blacklivesmatter.policebrutality.data.model.Incident import com.blacklivesmatter.policebrutality.data.model.LocationIncidents import com.blacklivesmatter.policebrutality.ui.extensions.LiveEvent -import com.google.firebase.crashlytics.FirebaseCrashlytics import kotlinx.coroutines.launch import org.threeten.bp.Instant import org.threeten.bp.OffsetDateTime @@ -21,9 +21,8 @@ import org.threeten.bp.format.DateTimeFormatter import org.threeten.bp.format.FormatStyle import timber.log.Timber import java.util.concurrent.TimeUnit -import javax.inject.Inject -class LocationViewModel @Inject constructor( +class LocationViewModel @ViewModelInject constructor( private val incidentRepository: IncidentRepository, private val analytics: Analytics ) : ViewModel() { @@ -88,8 +87,7 @@ class LocationViewModel @Inject constructor( val incidents: List = try { incidentRepository.getIncidentsCoroutine() } catch (error: Exception) { - // Report error so that I get notified to fix it. - FirebaseCrashlytics.getInstance().recordException(error) + Timber.e(error, "Unable to process API request.") emptyList() } diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/launcher/LauncherActivity.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/launcher/LauncherActivity.kt index c7e4d35..208ccba 100644 --- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/launcher/LauncherActivity.kt +++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/launcher/LauncherActivity.kt @@ -5,23 +5,19 @@ import android.os.Bundle import android.view.View import android.widget.Toast import androidx.activity.viewModels -import androidx.lifecycle.ViewModelProvider +import androidx.appcompat.app.AppCompatActivity import com.blacklivesmatter.policebrutality.MainActivity import com.blacklivesmatter.policebrutality.R import com.blacklivesmatter.policebrutality.ui.extensions.observeKotlin -import dagger.android.support.DaggerAppCompatActivity -import javax.inject.Inject +import dagger.hilt.android.AndroidEntryPoint /** * Shows splash screen. Splash screen provided by window background, * so that the image is immediately visible. */ -class LauncherActivity : DaggerAppCompatActivity() { - - @Inject - internal lateinit var viewModelFactory: ViewModelProvider.Factory - - private val viewModel: LauncherViewModel by viewModels { viewModelFactory } +@AndroidEntryPoint +class LauncherActivity : AppCompatActivity() { + private val viewModel: LauncherViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/launcher/LauncherViewModel.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/launcher/LauncherViewModel.kt index 1421487..3b47c37 100644 --- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/launcher/LauncherViewModel.kt +++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/launcher/LauncherViewModel.kt @@ -1,6 +1,7 @@ package com.blacklivesmatter.policebrutality.ui.launcher import android.content.SharedPreferences +import androidx.hilt.lifecycle.ViewModelInject import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -8,17 +9,16 @@ import com.blacklivesmatter.policebrutality.ui.extensions.LiveEvent import kotlinx.coroutines.delay import kotlinx.coroutines.launch import timber.log.Timber -import javax.inject.Inject /** * View model for [LauncherActivity]. */ -class LauncherViewModel @Inject constructor( +class LauncherViewModel @ViewModelInject constructor( private val preferences: SharedPreferences ) : ViewModel() { companion object { - private const val SPLASH_SCREEN_DELAY_FIRST_TIME_MS = 3000L - private const val SPLASH_SCREEN_DELAY_MS = 1000L + private const val SPLASH_SCREEN_DELAY_FIRST_TIME_MS = 3500L + private const val SPLASH_SCREEN_DELAY_MS = 1200L private const val PREF_KEY_FIRST_LAUNCH = "preference_key_first_time_app_launch" } 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 04425d4..1f61678 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 @@ -12,8 +12,8 @@ import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.core.view.children +import androidx.fragment.app.Fragment 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 @@ -22,19 +22,16 @@ import com.blacklivesmatter.policebrutality.ui.util.IntentBuilder import com.google.android.material.chip.Chip import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar -import dagger.android.support.DaggerFragment +import dagger.hilt.android.AndroidEntryPoint import timber.log.Timber import javax.inject.Inject -class MoreInfoFragment : DaggerFragment() { - - @Inject - lateinit var viewModelFactory: ViewModelProvider.Factory - +@AndroidEntryPoint +class MoreInfoFragment : Fragment() { @Inject lateinit var analytics: Analytics - private val viewModel by viewModels { viewModelFactory } + private val viewModel by viewModels() private lateinit var viewDataBinding: FragmentMoreInfoBinding override fun onCreate(savedInstanceState: Bundle?) { diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/moreinfo/MoreInfoViewModel.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/moreinfo/MoreInfoViewModel.kt index fbf86c7..c6471dd 100644 --- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/moreinfo/MoreInfoViewModel.kt +++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/moreinfo/MoreInfoViewModel.kt @@ -1,5 +1,6 @@ package com.blacklivesmatter.policebrutality.ui.moreinfo +import androidx.hilt.lifecycle.ViewModelInject import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModel import com.blacklivesmatter.policebrutality.R @@ -9,10 +10,9 @@ import com.blacklivesmatter.policebrutality.config.PB_LINK_REDDIT import com.blacklivesmatter.policebrutality.config.PB_LINK_TWITTER import com.blacklivesmatter.policebrutality.config.PB_LINK_WEB import com.blacklivesmatter.policebrutality.ui.extensions.LiveEvent -import javax.inject.Inject import timber.log.Timber -class MoreInfoViewModel @Inject constructor() : ViewModel() { +class MoreInfoViewModel @ViewModelInject constructor() : ViewModel() { private val _openExternalUrl = LiveEvent() val openExternalUrl: LiveData = _openExternalUrl 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 4955c05..f08a887 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 @@ -6,8 +6,8 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.text.HtmlCompat +import androidx.fragment.app.Fragment 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 @@ -15,17 +15,15 @@ import com.blacklivesmatter.policebrutality.ui.extensions.observeKotlin import com.blacklivesmatter.policebrutality.ui.util.IntentBuilder import com.google.android.material.snackbar.Snackbar import com.google.android.material.textview.MaterialTextView -import dagger.android.support.DaggerFragment +import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject -class NewReportFragment : DaggerFragment() { - @Inject - lateinit var viewModelFactory: ViewModelProvider.Factory - +@AndroidEntryPoint +class NewReportFragment : Fragment() { @Inject lateinit var analytics: Analytics - private val viewModel by viewModels { viewModelFactory } + private val viewModel by viewModels() private lateinit var viewDataBinding: FragmentNewReportBinding override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 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 5e5ec1f..666368f 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 @@ -1,13 +1,13 @@ package com.blacklivesmatter.policebrutality.ui.newreport +import androidx.hilt.lifecycle.ViewModelInject 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( +class NewReportViewModel @ViewModelInject constructor( private val analytics: Analytics ) : ViewModel() { private val _openReportIncidentUrl = LiveEvent() diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/util/LinkTransformer.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/util/LinkTransformer.kt index dd6bc3a..0facae3 100644 --- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/util/LinkTransformer.kt +++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/ui/util/LinkTransformer.kt @@ -17,7 +17,8 @@ object LinkTransformer { "www.youtube.com" to R.drawable.ic_web_youtube, "v.redd.it" to R.drawable.ic_web_reddit, "old.reddit.com" to R.drawable.ic_web_reddit, - "www.reddit.com" to R.drawable.ic_web_reddit + "www.reddit.com" to R.drawable.ic_web_reddit, + "vimeo.com" to R.drawable.ic_web_vimeo ).withDefault { R.drawable.ic_outline_web } /** diff --git a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/worker/SeedDatabaseWorker.kt b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/worker/SeedDatabaseWorker.kt index 67f5256..b22cb34 100644 --- a/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/worker/SeedDatabaseWorker.kt +++ b/android-app/app/src/main/java/com/blacklivesmatter/policebrutality/worker/SeedDatabaseWorker.kt @@ -1,31 +1,24 @@ package com.blacklivesmatter.policebrutality.worker import android.content.Context +import androidx.hilt.Assisted +import androidx.hilt.work.WorkerInject import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.blacklivesmatter.policebrutality.config.INCIDENT_DATA_FILENAME -import com.blacklivesmatter.policebrutality.data.AppDatabase +import com.blacklivesmatter.policebrutality.data.IncidentDao import com.blacklivesmatter.policebrutality.data.model.IncidentsSource import com.google.gson.Gson import com.google.gson.stream.JsonReader -import dagger.android.HasAndroidInjector import kotlinx.coroutines.coroutineScope import timber.log.Timber -import javax.inject.Inject -class SeedDatabaseWorker( - context: Context, - workerParams: WorkerParameters +class SeedDatabaseWorker @WorkerInject constructor( + @Assisted context: Context, + @Assisted workerParams: WorkerParameters, + private val gson: Gson, + private val incidentDao: IncidentDao ) : CoroutineWorker(context, workerParams) { - init { - // Inject into Workers (androidx.WorkManager API) - // https://github.com/google/dagger/issues/1183#issuecomment-601158396 - val injector = context.applicationContext as HasAndroidInjector - injector.androidInjector().inject(this) - } - - @Inject - internal lateinit var gson: Gson override suspend fun doWork(): Result = coroutineScope { try { @@ -34,8 +27,7 @@ class SeedDatabaseWorker( val incidents = gson.fromJson(jsonReader, IncidentsSource::class.java) Timber.i("Processed ${incidents.data.size} incidents from JSON.") - val database = AppDatabase.getInstance(applicationContext) - database.incidentDao().insertAll(incidents.data) + incidentDao.insertAll(incidents.data) Result.success() } diff --git a/android-app/app/src/main/res/anim/fade_in.xml b/android-app/app/src/main/res/anim/fade_in.xml index ae2fb9b..06290db 100644 --- a/android-app/app/src/main/res/anim/fade_in.xml +++ b/android-app/app/src/main/res/anim/fade_in.xml @@ -1,5 +1,7 @@ - + - + - + - + - - diff --git a/android-app/app/src/main/res/drawable/ic_fist_raised.xml b/android-app/app/src/main/res/drawable/ic_fist_raised.xml index 6b47c05..dfa5739 100644 --- a/android-app/app/src/main/res/drawable/ic_fist_raised.xml +++ b/android-app/app/src/main/res/drawable/ic_fist_raised.xml @@ -1,9 +1,11 @@ + android:pathData="M255.98,160L255.98,16c0,-8.84 -7.16,-16 -16,-16h-32c-8.84,0 -16,7.16 -16,16v146.93c5.02,-1.78 10.34,-2.93 15.97,-2.93h48.03zM383.98,255.99c-0.01,-35.34 -28.66,-63.99 -63.99,-63.99L207.85,192c-8.78,0 -15.9,7.07 -15.9,15.85v0.56c0,26.27 21.3,47.59 47.57,47.59h35.26c9.68,0 13.2,3.58 13.2,8v16.2c0,4.29 -3.59,7.78 -7.88,8 -44.52,2.28 -64.16,24.71 -96.05,72.55l-6.31,9.47a7.994,7.994 0,0 1,-11.09 2.22l-13.31,-8.88a7.994,7.994 0,0 1,-2.22 -11.09l6.31,-9.47c15.73,-23.6 30.2,-43.26 47.31,-58.08 -17.27,-5.51 -31.4,-18.12 -38.87,-34.45 -6.59,3.41 -13.96,5.52 -21.87,5.52h-32c-12.34,0 -23.49,-4.81 -32,-12.48C71.48,251.19 60.33,256 48,256L16,256c-5.64,0 -10.97,-1.15 -16,-2.95v77.93c0,33.95 13.48,66.5 37.49,90.51L63.99,448v64h255.98v-63.96l35.91,-35.92A96.035,96.035 0,0 0,384 344.21l-0.02,-88.22zM351.97,165.9L351.97,48c0,-8.84 -7.16,-16 -16,-16h-32c-8.84,0 -16,7.16 -16,16v112h32c11.28,0 21.94,2.31 32,5.9zM16,224h32c8.84,0 16,-7.16 16,-16L64,80c0,-8.84 -7.16,-16 -16,-16L16,64C7.16,64 0,71.16 0,80v128c0,8.84 7.16,16 16,16zM111.99,224h32c8.84,0 16,-7.16 16,-16L159.99,48c0,-8.84 -7.16,-16 -16,-16h-32c-8.84,0 -16,7.16 -16,16v160c0,8.84 7.16,16 16,16z" + tools:ignore="VectorPath" /> diff --git a/android-app/app/src/main/res/drawable/ic_hashtag.xml b/android-app/app/src/main/res/drawable/ic_hashtag.xml deleted file mode 100644 index 622e41c..0000000 --- a/android-app/app/src/main/res/drawable/ic_hashtag.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android-app/app/src/main/res/drawable/ic_home_black_24dp.xml b/android-app/app/src/main/res/drawable/ic_home_black_24dp.xml deleted file mode 100644 index f8bb0b5..0000000 --- a/android-app/app/src/main/res/drawable/ic_home_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android-app/app/src/main/res/drawable/ic_launcher_background.xml b/android-app/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 07d5da9..0000000 --- a/android-app/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android-app/app/src/main/res/drawable/ic_notifications_black_24dp.xml b/android-app/app/src/main/res/drawable/ic_notifications_black_24dp.xml deleted file mode 100644 index 78b75c3..0000000 --- a/android-app/app/src/main/res/drawable/ic_notifications_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android-app/app/src/main/res/drawable/ic_outline_app_information_24.xml b/android-app/app/src/main/res/drawable/ic_outline_app_information_24.xml deleted file mode 100644 index 89abeb2..0000000 --- a/android-app/app/src/main/res/drawable/ic_outline_app_information_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/android-app/app/src/main/res/drawable/ic_outline_info_24.xml b/android-app/app/src/main/res/drawable/ic_outline_info_24.xml deleted file mode 100644 index 35f7f5f..0000000 --- a/android-app/app/src/main/res/drawable/ic_outline_info_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/android-app/app/src/main/res/drawable/ic_usa_map_outline.xml b/android-app/app/src/main/res/drawable/ic_usa_map_outline.xml new file mode 100644 index 0000000..032c8fe --- /dev/null +++ b/android-app/app/src/main/res/drawable/ic_usa_map_outline.xml @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/android-app/app/src/main/res/drawable/ic_web_instagram.xml b/android-app/app/src/main/res/drawable/ic_web_instagram.xml index 6c01825..23c3f02 100644 --- a/android-app/app/src/main/res/drawable/ic_web_instagram.xml +++ b/android-app/app/src/main/res/drawable/ic_web_instagram.xml @@ -1,4 +1,11 @@ - - + + diff --git a/android-app/app/src/main/res/drawable/ic_web_reddit.xml b/android-app/app/src/main/res/drawable/ic_web_reddit.xml index 3279ba5..74776ba 100644 --- a/android-app/app/src/main/res/drawable/ic_web_reddit.xml +++ b/android-app/app/src/main/res/drawable/ic_web_reddit.xml @@ -1,4 +1,11 @@ - - + + diff --git a/android-app/app/src/main/res/drawable/ic_web_twitter.xml b/android-app/app/src/main/res/drawable/ic_web_twitter.xml index a62bd45..e7086ea 100644 --- a/android-app/app/src/main/res/drawable/ic_web_twitter.xml +++ b/android-app/app/src/main/res/drawable/ic_web_twitter.xml @@ -1,4 +1,11 @@ - - + + diff --git a/android-app/app/src/main/res/drawable/ic_web_vimeo.xml b/android-app/app/src/main/res/drawable/ic_web_vimeo.xml index 9c86c0b..4b68d23 100644 --- a/android-app/app/src/main/res/drawable/ic_web_vimeo.xml +++ b/android-app/app/src/main/res/drawable/ic_web_vimeo.xml @@ -1,4 +1,9 @@ - - + + diff --git a/android-app/app/src/main/res/layout/activity_launcher.xml b/android-app/app/src/main/res/layout/activity_launcher.xml index 72e9f94..3fe1835 100644 --- a/android-app/app/src/main/res/layout/activity_launcher.xml +++ b/android-app/app/src/main/res/layout/activity_launcher.xml @@ -7,7 +7,7 @@ android:layout_margin="@dimen/view_spacing_xxlarge"> + + + app:layout_constraintTop_toBottomOf="@+id/hashtag_2" /> diff --git a/android-app/app/src/main/res/layout/activity_main.xml b/android-app/app/src/main/res/layout/activity_main.xml index 05bc07f..d4174f2 100644 --- a/android-app/app/src/main/res/layout/activity_main.xml +++ b/android-app/app/src/main/res/layout/activity_main.xml @@ -1,6 +1,7 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> + app:navGraph="@navigation/mobile_navigation" + tools:ignore="FragmentTagUsage" /> \ No newline at end of file diff --git a/android-app/app/src/main/res/layout/fragment_more_info.xml b/android-app/app/src/main/res/layout/fragment_more_info.xml index 4a3ea3c..3a57cd2 100644 --- a/android-app/app/src/main/res/layout/fragment_more_info.xml +++ b/android-app/app/src/main/res/layout/fragment_more_info.xml @@ -52,7 +52,6 @@ android:layout_height="wrap_content" android:layout_gravity="bottom" android:layout_marginStart="32dp" - android:layout_marginLeft="32dp" android:layout_marginEnd="32dp" android:layout_marginBottom="32dp" android:gravity="end" diff --git a/android-app/app/src/main/res/layout/fragment_more_info_content.xml b/android-app/app/src/main/res/layout/fragment_more_info_content.xml index 4845223..404acb1 100644 --- a/android-app/app/src/main/res/layout/fragment_more_info_content.xml +++ b/android-app/app/src/main/res/layout/fragment_more_info_content.xml @@ -56,7 +56,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="@dimen/view_spacing_large" - android:text="Most used hash tags" + android:text="@string/label_most_used_hash_tags" app:layout_constraintEnd_toStartOf="@+id/guideline_end" app:layout_constraintStart_toStartOf="@+id/guideline_start" app:layout_constraintTop_toBottomOf="@+id/pb2020_ext_link_web" /> diff --git a/android-app/app/src/main/res/menu/locations_menu.xml b/android-app/app/src/main/res/menu/locations_menu.xml index 27a4cd4..6389e38 100644 --- a/android-app/app/src/main/res/menu/locations_menu.xml +++ b/android-app/app/src/main/res/menu/locations_menu.xml @@ -13,6 +13,6 @@ diff --git a/android-app/app/src/main/res/values/colors.xml b/android-app/app/src/main/res/values/colors.xml index 127eb00..3987cc4 100644 --- a/android-app/app/src/main/res/values/colors.xml +++ b/android-app/app/src/main/res/values/colors.xml @@ -1,5 +1,5 @@ - + #FFBB86FC #FF6200EE diff --git a/android-app/app/src/main/res/values/dimens.xml b/android-app/app/src/main/res/values/dimens.xml index 57e4369..d4f9353 100644 --- a/android-app/app/src/main/res/values/dimens.xml +++ b/android-app/app/src/main/res/values/dimens.xml @@ -1,4 +1,7 @@ - + + + 3 + 16dp 16dp diff --git a/android-app/app/src/main/res/values/strings.xml b/android-app/app/src/main/res/values/strings.xml index eeedb5e..d268e2e 100644 --- a/android-app/app/src/main/res/values/strings.xml +++ b/android-app/app/src/main/res/values/strings.xml @@ -1,4 +1,4 @@ - + 2020PB @@ -6,7 +6,6 @@ Incidents at %s Incidents on %s Report Incident - Notifications Additional Resources Filter incident by specific date @@ -14,7 +13,7 @@ Incidents Report Info - About App + About App Share App Filter by Date Refresh @@ -22,12 +21,12 @@ The %s hash tag has been copied to your clipboard. Unable to load selected URL due to internal issue. Try different external URLs. - Unable to load report incident from app. Visit original GitHub repository to report incident. See \"More Info\" section for additional resources. + Unable to load report incident from app. Visit original GitHub repository to report incident. See “More Info” section for additional resources. Sorry, there are no incidents reported on selected date %1$s - Successfully refreshed %1$s incidents from source \'2020PB/police-brutality\'. + 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? + Did you know, you can share a incident details by just tapping on the item from list? Justice for George Floyd protest fist image icon @@ -52,7 +51,7 @@ #BlackLivesMatter\n#JusticeForGeorgeFloyd\n#PoliceBrutality - Total %d reported incidents. + Total %d reported incidents. at %1$s, %2$s @@ -68,7 +67,7 @@

Please read the Contribution Guidelines first to find out the Incident Report Guidelines and Frequently Asked Questions (FAQ).

]]>
- NOTE: This opens link on your browser app. You will need GitHub account to report incident in the \'2020PB/police-brutality\' GitHub repository. + NOTE: This opens link on your browser app. You will need GitHub account to report incident in the ‘2020PB/police-brutality’ GitHub repository. … a collection of Police brutality incidents during George Floyd protests ✊ @@ -83,4 +82,5 @@ \n\n \nCredits: All the data is reported via https://github.com/2020PB/police-brutality
+ Most used hash tags \ No newline at end of file diff --git a/android-app/build.gradle b/android-app/build.gradle index 2823ac9..b727940 100644 --- a/android-app/build.gradle +++ b/android-app/build.gradle @@ -7,12 +7,11 @@ buildscript { targetSdkVersion = 29 // App dependencies + androidXHiltVersion = '1.0.0-alpha01' appCompatVersion = '1.1.0' cardViewVersion = '1.0.0' - constraintLayoutVersion = '2.0.0-beta6' + constraintLayoutVersion = '2.0.0-beta7' coreTestingVersion = '2.0.0' - coroutinesVersion = "1.3.0-M2" - dagger2Version = '2.27' espressoVersion = '3.1.1' firebaseAnalyticsVersion = '17.4.3' firebaseCrashlyticsGradle = '2.1.1' @@ -21,7 +20,7 @@ buildscript { glideVersion = '4.11.0' googleGmsServices = '4.3.3' gradleVersion = '4.0.0' - gsonVersion = '2.8.6' + hiltVersion = '2.28-alpha' junitVersion = '4.13' kotlinVersion = '1.3.72' ktlintVersion = '0.33.0' @@ -36,7 +35,6 @@ buildscript { roomVersion = '2.2.5' runnerVersion = '1.0.1' testExtJunit = '1.1.0' - timberVersion = '4.7.1' truthVersion = '0.42' viewPagerVersion = '1.0.0' workVersion = '2.3.4' @@ -56,6 +54,7 @@ buildscript { classpath "com.google.gms:google-services:$googleGmsServices" classpath "com.google.firebase:firebase-crashlytics-gradle:$firebaseCrashlyticsGradle" classpath "org.jlleitschuh.gradle:ktlint-gradle:9.2.1" + classpath "com.google.dagger:hilt-android-gradle-plugin:$hiltVersion" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/resources/README.md b/resources/README.md index 71d8300..4ae841a 100644 --- a/resources/README.md +++ b/resources/README.md @@ -12,3 +12,4 @@ Poster source (credit): https://www.reddit.com/r/PlexPosters/comments/gx54fr/cou * https://commons.wikimedia.org/wiki/File:Fist.svg * https://unsplash.com/photos/_vAC0je-hKo * https://github.com/FortAwesome/Font-Awesome +* https://svgsilh.com/image/1674031.html