Commit 08d166cb authored by Javinator9889's avatar Javinator9889

Release v1.1.0 with OkHttp library for downloading files from network

parent a4f77103
......@@ -43,9 +43,10 @@ android {
applicationId "com.javinator9889.handwashingreminder"
minSdkVersion 17
targetSdkVersion 29
versionCode 94
versionName "1.0.1-${gitCommitHash}"
versionCode 100
versionName "1.1.0-${gitCommitHash}"
multiDexEnabled true
resConfigs "en", "es"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
......@@ -89,7 +90,7 @@ android {
preDexLibraries true
javaMaxHeapSize "1G"
}
dynamicFeatures = [":appintro", ":ads", ":bundledemoji"]
dynamicFeatures = [":appintro", ":ads", ":bundledemoji", ":okhttp", ":okhttplegacy"]
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
......@@ -100,7 +101,6 @@ android {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
}
dependencies {
......@@ -127,11 +127,6 @@ dependencies {
implementation 'androidx.recyclerview:recyclerview:1.1.0'
// https://developer.android.com/studio/build/multidex
implementation 'androidx.multidex:multidex:2.0.1'
// https://github.com/afollestad/material-dialogs
implementation 'com.afollestad.material-dialogs:core:3.3.0'
implementation 'com.afollestad.material-dialogs:datetime:3.3.0'
implementation 'com.afollestad.material-dialogs:lifecycle:3.3.0'
implementation 'com.afollestad.material-dialogs:bottomsheets:3.3.0'
// https://github.com/mikepenz/Android-Iconics
implementation 'com.mikepenz:iconics-core:5.0.2'
implementation 'com.mikepenz:iconics-views:5.0.2'
......@@ -190,4 +185,8 @@ dependencies {
implementation 'com.beust:klaxon:5.2'
// https://github.com/SufficientlySecure/html-textview
implementation 'org.sufficientlysecure:html-textview:3.9'
// https://square.github.io/okio/#releases
implementation 'com.squareup.okio:okio:2.5.0'
// https://github.com/google/conscrypt/
implementation 'org.conscrypt:conscrypt-android:2.4.0'
}
......@@ -36,6 +36,22 @@
*;
}
-keep class com.javinator9889.handwashingreminder.okhttp.OkHttpDownloader {
com.javinator9889.handwashingreminder.okhttp.OkHttpDownloader$Provider Provider;
}
-keep com.javinator9889.handwashingreminder.okhttp.OkHttpDownloader$Provider {
*;
}
-keep class com.javinator9889.handwashingreminder.okhttplegacy.OkHttpDownloader {
com.javinator9889.handwashingreminder.okhttplegacy.OkHttpDownloader$Provider Provider;
}
-keep com.javinator9889.handwashingreminder.okhttplegacy.OkHttpDownloader$Provider {
*;
}
-keep class com.javinator9889.handwashingreminder.bundledemoji.BundledEmojiConfig {
*;
}
......
......@@ -49,7 +49,9 @@ import com.mikepenz.iconics.Iconics
import javinator9889.localemanager.utils.languagesupport.LanguagesSupport
import kotlinx.android.synthetic.main.splash_screen.*
import kotlinx.coroutines.*
import org.conscrypt.Conscrypt
import timber.log.Timber
import java.security.Security
import java.util.*
import kotlin.collections.ArrayList
......@@ -138,13 +140,7 @@ class LauncherActivity : AppCompatActivity() {
if (app.sharedPreferences.getBoolean(ADS_ENABLED, true)) {
when (resultCode) {
Activity.RESULT_OK -> {
val className = "${Ads.PACKAGE_NAME}.${Ads
.CLASS_NAME}\$${Ads.PROVIDER_NAME}"
val adProvider = Class.forName(className).kotlin
.objectInstance as AdLoader.Provider
app.adLoader = adProvider.instance(app)
val adsEnabler = AdsEnabler(app)
adsEnabler.enableAds()
initAds()
data.notNull {
createPackageContext(packageName, 0).also {
SplitCompat.install(it)
......@@ -189,6 +185,10 @@ class LauncherActivity : AppCompatActivity() {
modules += AppIntro.MODULE_NAME
launchOnInstall = true
}
modules += if (isAtLeast(AndroidVersion.LOLLIPOP) && false)
OkHttp.MODULE_NAME
else
OkHttpLegacy.MODULE_NAME
if (googleApi.isGooglePlayServicesAvailable(
this,
GOOGLE_PLAY_SERVICES_MIN_VERSION
......@@ -213,6 +213,16 @@ class LauncherActivity : AppCompatActivity() {
startActivityForResult(intent, DYNAMIC_FEATURE_INSTALL_RESULT_CODE)
}
private fun initAds() {
val className = "${Ads.PACKAGE_NAME}.${Ads
.CLASS_NAME}\$${Ads.PROVIDER_NAME}"
val adProvider = Class.forName(className).kotlin
.objectInstance as AdLoader.Provider
app.adLoader = adProvider.instance(app)
val adsEnabler = AdsEnabler(app)
adsEnabler.enableAds()
}
private fun createDynamicFeatureActivityIntent(
modules: Array<String>,
launchOnInstall: Boolean = false,
......@@ -228,6 +238,9 @@ class LauncherActivity : AppCompatActivity() {
private fun initVariables() {
Timber.d("Initializing Iconics")
Iconics.init(this)
Timber.d("Setting-up security providers")
Security.insertProviderAt(Conscrypt.newProvider(), 1)
Timber.d("Setting-up activity recognition")
if (app.sharedPreferences.getBoolean(
Preferences.ACTIVITY_TRACKING_ENABLED, false
) && with(GoogleApiAvailability.getInstance()) {
......@@ -239,6 +252,7 @@ class LauncherActivity : AppCompatActivity() {
} else {
app.activityHandler.disableActivityTracker()
}
Timber.d("Initializing Billing Service")
app.billingService = BillingService(this)
try {
app.workHandler.enqueuePeriodicNotificationsWorker()
......@@ -246,6 +260,7 @@ class LauncherActivity : AppCompatActivity() {
} catch (_: UninitializedPropertyAccessException) {
Timber.i("Scheduler times have not been initialized")
}
Timber.d("Setting-up Firebase custom properties")
setupFirebaseProperties()
}
......@@ -261,22 +276,24 @@ class LauncherActivity : AppCompatActivity() {
with(firebaseRemoteConfig) {
Timber.d("Initializing Firebase Remote Config")
setConfigSettingsAsync(config)
setDefaultsAsync(when (Locale.getDefault().language) {
Locale(LanguagesSupport.Language.SPANISH).language -> {
firebaseAnalytics.setUserProperty(
Firebase.Properties.LANGUAGE,
LanguagesSupport.Language.SPANISH
)
R.xml.remote_config_defaults_es
}
else -> {
firebaseAnalytics.setUserProperty(
Firebase.Properties.LANGUAGE,
LanguagesSupport.Language.ENGLISH
)
R.xml.remote_config_defaults
setDefaultsAsync(
when (Locale.getDefault().language) {
Locale(LanguagesSupport.Language.SPANISH).language -> {
firebaseAnalytics.setUserProperty(
Firebase.Properties.LANGUAGE,
LanguagesSupport.Language.SPANISH
)
R.xml.remote_config_defaults_es
}
else -> {
firebaseAnalytics.setUserProperty(
Firebase.Properties.LANGUAGE,
LanguagesSupport.Language.ENGLISH
)
R.xml.remote_config_defaults
}
}
})
)
fetchAndActivate().addOnSuccessListener {
if (canFinishActivity)
finish()
......
......@@ -127,7 +127,6 @@ class SliderView : BaseFragmentView() {
GlideApp.with(this)
.load(drawableId)
.centerInside()
.centerCrop()
.into(image)
} catch (e: Exception) {
Timber.e(e, "Error while loading Glide view")
......
......@@ -24,6 +24,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.liveData
import com.google.firebase.perf.metrics.AddTrace
import com.javinator9889.handwashingreminder.application.HandwashingApplication
import com.javinator9889.handwashingreminder.network.HttpDownloader
import com.javinator9889.handwashingreminder.utils.Videos.URI.FILENAME
import com.javinator9889.handwashingreminder.utils.Videos.URI.HASH
import com.javinator9889.handwashingreminder.utils.Videos.URI.URL
......@@ -32,10 +33,14 @@ import com.javinator9889.handwashingreminder.utils.isConnected
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.withContext
import okio.HashingSink
import okio.buffer
import okio.sink
import timber.log.Timber
import java.io.*
import java.io.File
import java.io.FileInputStream
import java.io.InputStream
import java.math.BigInteger
import java.net.URL
import java.security.MessageDigest
import javax.inject.Inject
......@@ -78,32 +83,20 @@ class VideoModel(
): File {
val file = File(cachePath, name)
withContext(Dispatchers.IO) {
val digest = MessageDigest.getInstance("SHA-256")
if (!isConnected() || !needsToDownloadFile(file, hash))
return@withContext
else
file.createNewFile()
val hashingSink = HashingSink.sha256(file.sink())
do {
if (!isConnected() || !needsToDownloadFile(file, hash))
return@withContext
else
file.createNewFile()
val stream = with(URL(url)) {
openStream()
}.let { BufferedInputStream(it, 8192) }
val data = ByteArray(8192)
val output = FileOutputStream(file)
try {
var count = stream.read(data)
while (count > 0) {
output.write(data, 0, count)
digest.update(data, 0, count)
count = stream.read(data)
val okHttpDownloader = HttpDownloader.newInstance()
with(okHttpDownloader.downloadFile(url)) {
hashingSink.buffer().use {
it.writeAll(this)
}
output.flush()
} catch (e: IOException) {
Timber.e(e, "Unable to download video $name")
} finally {
output.close()
stream.close()
close()
}
} while (!sameSHA2Hash(hash, digest))
} while (!hashingSink.hash.hex().equals(hash, true))
}
return file
}
......
......@@ -22,7 +22,9 @@ import android.content.Context;
import androidx.annotation.NonNull;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.Registry;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.load.DecodeFormat;
import com.bumptech.glide.module.AppGlideModule;
......@@ -37,4 +39,10 @@ public final class CustomGraphicsModule extends AppGlideModule {
.format(DecodeFormat.PREFER_ARGB_8888)
);
}
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
// super.registerComponents(context, glide, registry);
// glide.getRegistry().
}
}
/*
* Copyright © 2020 - present | Handwashing reminder by Javinator9889
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*
* Created by Javinator9889 on 21/04/20 - Handwashing reminder.
*/
package com.javinator9889.handwashingreminder.network
import com.javinator9889.handwashingreminder.utils.AndroidVersion
import com.javinator9889.handwashingreminder.utils.OkHttp
import com.javinator9889.handwashingreminder.utils.OkHttpLegacy
import com.javinator9889.handwashingreminder.utils.isAtLeast
object HttpDownloader {
fun newInstance(): OkHttpDownloader {
val className = if (isAtLeast(AndroidVersion.LOLLIPOP) && false)
"${OkHttp.PACKAGE_NAME}.${OkHttp.CLASS_NAME}\$${OkHttp.PROVIDER_NAME}"
else
"${OkHttpLegacy.PACKAGE_NAME}.${OkHttpLegacy
.CLASS_NAME}\$${OkHttpLegacy.PROVIDER_NAME}"
val okHttpProvider = Class.forName(className).kotlin.objectInstance
as OkHttpDownloader.Provider
return okHttpProvider.newInstance()
}
}
\ No newline at end of file
/*
* Copyright © 2020 - present | Handwashing reminder by Javinator9889
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*
* Created by Javinator9889 on 21/04/20 - Handwashing reminder.
*/
package com.javinator9889.handwashingreminder.network
import okio.BufferedSource
interface OkHttpDownloader {
interface Provider {
fun newInstance(): OkHttpDownloader
}
fun downloadFile(url: String): BufferedSource
}
\ No newline at end of file
......@@ -79,6 +79,24 @@ class BundledEmoji {
}
}
class OkHttp {
companion object Modules {
const val MODULE_NAME = "okhttp"
const val PACKAGE_NAME = "com.javinator9889.handwashingreminder.okhttp"
const val CLASS_NAME = "OkHttpDownloader"
const val PROVIDER_NAME = "Provider"
}
}
class OkHttpLegacy {
companion object Modules {
const val MODULE_NAME = "okhttplegacy"
const val PACKAGE_NAME = "com.javinator9889.handwashingreminder.okhttplegacy"
const val CLASS_NAME = "OkHttpDownloader"
const val PROVIDER_NAME = "Provider"
}
}
class RemoteConfig {
companion object Keys {
const val SPECIAL_EVENT = "special_event"
......
......@@ -275,4 +275,6 @@
<string name="prevention">Prevention</string>
<string name="written_by">Written by %s</string>
<string name="available_at">Available at: %s</string>
<string name="title_okhttp" translatable="false">OkHttp</string>
<string name="title_okhttplegacy" translatable="false">Ok HTTP Legacy</string>
</resources>
......@@ -30,10 +30,10 @@ import android.widget.TextView
import android.widget.TimePicker
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.ViewCompat
import com.bumptech.glide.Glide
import com.google.android.play.core.splitcompat.SplitCompat
import com.javinator9889.handwashingreminder.activities.support.ActionBarBase
import com.javinator9889.handwashingreminder.appintro.R
import com.javinator9889.handwashingreminder.graphics.GlideApp
import com.javinator9889.handwashingreminder.utils.AndroidVersion
import com.javinator9889.handwashingreminder.utils.TimeConfig
import com.javinator9889.handwashingreminder.utils.formatTime
......@@ -112,9 +112,8 @@ class TimeConfigActivity :
}
if (imageRes != null)
try {
GlideApp.with(this)
Glide.with(this)
.load(imageRes)
.centerCrop()
.centerInside()
.into(image)
} catch (e: Exception) {
......
......@@ -23,9 +23,9 @@ import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.google.android.material.card.MaterialCardView
import com.javinator9889.handwashingreminder.appintro.R
import com.javinator9889.handwashingreminder.graphics.GlideApp
import com.javinator9889.handwashingreminder.listeners.ViewHolder
import com.javinator9889.handwashingreminder.utils.TimeConfig
import com.mikepenz.iconics.view.IconicsImageView
......@@ -112,10 +112,9 @@ class TimeConfigViewHolder(private val view: View) :
private fun loadImageView(@DrawableRes imageRes: Int?) {
if (imageRes != null)
try {
GlideApp.with(view)
Glide.with(view)
.load(imageRes)
.centerInside()
.centerCrop()
.into(image)
} catch (e: Exception) {
Timber.e(e, "Error while loading Glide view")
......
apply plugin: 'com.android.dynamic-feature'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
......
apply plugin: 'com.android.dynamic-feature'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
defaultConfig {
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':app')
implementation "androidx.core:core-ktx:1.2.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
// https://square.github.io/okhttp/#releases
implementation 'com.squareup.okhttp3:okhttp:4.5.0'
}
repositories {
mavenCentral()
}
<manifest xmlns:dist="http://schemas.android.com/apk/distribution"
package="com.javinator9889.handwashingreminder.okhttp">
<dist:module
dist:instant="false"
dist:title="@string/title_okhttp">
<dist:delivery>
<dist:on-demand />
</dist:delivery>
<dist:fusing dist:include="false" />
</dist:module>
</manifest>
/*
* Copyright © 2020 - present | Handwashing reminder by Javinator9889
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*
* Created by Javinator9889 on 21/04/20 - Handwashing reminder.
*/
package com.javinator9889.handwashingreminder.okhttp
import com.javinator9889.handwashingreminder.network.OkHttpDownloader
import okhttp3.CacheControl
import okhttp3.OkHttpClient
import okhttp3.Request
import okio.BufferedSource
import java.io.IOException
class OkHttpDownloader : OkHttpDownloader {
private val client = OkHttpClient()
companion object Provider : OkHttpDownloader.Provider {
override fun newInstance(): OkHttpDownloader = OkHttpDownloader()
}
override fun downloadFile(url: String): BufferedSource {
val request = with(Request.Builder()) {
url(url)
cacheControl(CacheControl.FORCE_NETWORK)
build()
}
with(client.newCall(request).execute()) {
if (!isSuccessful) {
close()
throw IOException("Unexpected code $this")
}
return body!!.source()
}
}
}
\ No newline at end of file
apply plugin: 'com.android.dynamic-feature'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
defaultConfig {
minSdkVersion 17
targetSdkVersion 29
versionCode 1
versionName "1.0"
}
buildTypes {
release {
proguardFiles 'okhttp3.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':app')
implementation "androidx.core:core-ktx:1.2.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
// https://github.com/square/okhttp/tree/okhttp_3.12.x
//noinspection GradleDependency
implementation 'com.squareup.okhttp3:okhttp:3.12.0'
}
repositories {
mavenCentral()
}
# JSR 305 annotations are for embedding nullability information.
-dontwarn javax.annotation.**
# A resource is loaded with a relative path so the package of this class must be preserved.
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
-dontwarn org.codehaus.mojo.animal_sniffer.*
# OkHttp platform used only on JVM and when Conscrypt dependency is available.
-dontwarn okhttp3.internal.platform.ConscryptPlatform
<manifest xmlns:dist="http://schemas.android.com/apk/distribution"
package="com.javinator9889.handwashingreminder.okhttplegacy">
<dist:module
dist:instant="false"
dist:title="@string/title_okhttplegacy">
<dist:delivery>
<dist:on-demand />
</dist:delivery>
<dist:fusing dist:include="true" />
</dist:module>
</manifest>
/*
* Copyright © 2020 - present | Handwashing reminder by Javinator9889
*