recently i have bought a source code and applying some changes and tested the app, everything worked perfect! except that i don't actually get notifications on the app the whole time but i will find a solution for that later... after trying to upload my app on Google Play it said that i have to have API level 29 as a minimum while i'm only having 27, i changed it by going to Project Structure and following the steps and when i tested the app on my phone, it opens normally but once i click on "menu" button it get crashed! i have searched a lot about the errors i found on the logcat but nothing worked for me. Hope you help me.
Errors on Logcat :
2020-07-22 14:43:07.004 9831-9831/? E/Zygote: isWhitelistProcess - Process is Whitelisted
2020-07-22 14:43:07.006 9831-9831/? E/Zygote: accessInfo : 1
2020-07-22 14:43:11.641 9831-9831/com.reflection.water.time E/chromium: [ERROR:filesystem_posix.cc(62)] mkdir /data/user/0/com.reflection.water.time/cache/WebView/Crashpad: No such file or directory (2)
2020-07-22 14:43:23.714 9831-9831/com.reflection.water.time E/chromium: [ERROR:aw_browser_terminator.cc(125)] Renderer process (9974) crash detected (code -1).
2020-07-22 14:43:28.232 9831-9831/com.reflection.water.time E/ViewRootImpl: sendUserActionEvent() returned.
2020-07-22 14:43:33.482 9831-9831/com.reflection.water.time E/ViewRootImpl: sendUserActionEvent() returned.
2020-07-22 14:43:44.827 9831-9831/com.reflection.water.time E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.reflection.water.time, PID: 9831
java.lang.IllegalArgumentException: Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed
at android.graphics.Canvas.checkValidClipOp(Canvas.java:779)
at android.graphics.Canvas.clipPath(Canvas.java:1007)
at co.ceryle.radiorealbutton.RoundedCornerLayout.dispatchDraw(RoundedCornerLayout.java:80)
at android.view.View.buildDrawingCacheImpl(View.java:21131)
at android.view.View.buildDrawingCache(View.java:20997)
at android.view.View.draw(View.java:21586)
at android.view.ViewGroup.drawChild(ViewGroup.java:4558)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4333)
at androidx.constraintlayout.widget.ConstraintLayout.dispatchDraw(ConstraintLayout.java:2023)
at android.view.View.draw(View.java:21871)
at android.view.View.updateDisplayListIfDirty(View.java:20741)
at android.view.View.draw(View.java:21594)
at android.view.ViewGroup.drawChild(ViewGroup.java:4558)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4333)
at android.view.View.draw(View.java:21871)
at android.view.View.updateDisplayListIfDirty(View.java:20741)
at android.view.View.draw(View.java:21594)
at android.view.ViewGroup.drawChild(ViewGroup.java:4558)
at androidx.coordinatorlayout.widget.CoordinatorLayout.drawChild(CoordinatorLayout.java:1246)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4333)
at android.view.View.updateDisplayListIfDirty(View.java:20727)
at android.view.View.draw(View.java:21594)
at android.view.ViewGroup.drawChild(ViewGroup.java:4558)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4333)
at android.view.View.updateDisplayListIfDirty(View.java:20727)
at android.view.View.draw(View.java:21594)
at android.view.ViewGroup.drawChild(ViewGroup.java:4558)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4333)
at android.view.View.updateDisplayListIfDirty(View.java:20727)
at android.view.View.draw(View.java:21594)
at android.view.ViewGroup.drawChild(ViewGroup.java:4558)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4333)
at android.view.View.updateDisplayListIfDirty(View.java:20727)
at android.view.View.draw(View.java:21594)
at android.view.ViewGroup.drawChild(ViewGroup.java:4558)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4333)
at android.view.View.updateDisplayListIfDirty(View.java:20727)
at android.view.View.draw(View.java:21594)
at android.view.ViewGroup.drawChild(ViewGroup.java:4558)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4333)
at android.view.View.updateDisplayListIfDirty(View.java:20727)
at android.view.View.draw(View.java:21594)
at android.view.ViewGroup.drawChild(ViewGroup.java:4558)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4333)
at android.view.View.draw(View.java:21871)
at com.android.internal.policy.DecorView.draw(DecorView.java:1129)
at android.view.View.updateDisplayListIfDirty(View.java:20741)
at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:725)
at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:731)
at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:840)
at android.view.ViewRootImpl.draw(ViewRootImpl.java:3968)
at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:3742)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3053)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1916)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8554)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:949)
at android.view.Choreographer.doCallbacks(Choreographer.java:761)
at android.view.Choreographer.doFrame(Choreographer.java:696)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:935)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7078)
2020-07-22 14:43:44.827 9831-9831/com.reflection.water.time E/AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965)
AndroidManifest.xml :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.reflection.water.time">
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name="com.reflection.water.time.WalkThroughActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name="com.reflection.water.time.StatsActivity"
android:theme="@style/MainTheme"
android:screenOrientation="portrait">
</activity>
<activity android:name="com.reflection.water.time.InitUserInfoActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name="com.reflection.water.time.MainActivity"
android:theme="@style/MainTheme"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<receiver android:name="com.reflection.water.time.recievers.NotifierReceiver"/>
<receiver android:name="com.reflection.water.time.recievers.BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
<meta-data
android:name="com.google.android.gms.ads.AD_MANAGER_APP"
android:value="true"/>
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-3940256099942544~3347511713"/>
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
</application>
</manifest>
MainActivity :
package com.reflection.water.time
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.os.Handler
import android.text.TextUtils
import android.util.TypedValue
import android.view.LayoutInflater
import android.widget.LinearLayout
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.reflection.water.time.fragments.BottomSheetFragment
import com.reflection.water.time.helpers.AlarmHelper
import com.reflection.water.time.helpers.SqliteHelper
import com.reflection.water.time.utils.AdsUtility
import com.reflection.water.time.utils.AppUtils
import com.daimajia.androidanimations.library.Techniques
import com.daimajia.androidanimations.library.YoYo
import com.google.android.gms.ads.MobileAds
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.textfield.TextInputLayout
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
private var totalIntake: Int = 0
private var inTook: Int = 0
private lateinit var sharedPref: SharedPreferences
private lateinit var sqliteHelper: SqliteHelper
private lateinit var dateNow: String
private var notificStatus: Boolean = false
private var selectedOption: Int? = null
private var snackbar: Snackbar? = null
private var doubleBackToExitPressedOnce = false
private lateinit var banner : LinearLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
MobileAds.initialize(this, AdsUtility.admobAppId)
sharedPref = getSharedPreferences(AppUtils.USERS_SHARED_PREF, AppUtils.PRIVATE_MODE)
sqliteHelper =
SqliteHelper(this)
AdsUtility.InterstitialAdmob(this)
totalIntake = sharedPref.getInt(AppUtils.TOTAL_INTAKE, 0)
if (sharedPref.getBoolean(AppUtils.FIRST_RUN_KEY, true)) {
startActivity(Intent(this, WalkThroughActivity::class.java))
finish()
} else if (totalIntake <= 0) {
startActivity(Intent(this, InitUserInfoActivity::class.java))
finish()
}
dateNow = AppUtils.getCurrentDate()!!
banner = findViewById(R.id.banner)
AdsUtility.admobBannerCall(this, banner)
}
fun updateValues() {
totalIntake = sharedPref.getInt(AppUtils.TOTAL_INTAKE, 0)
inTook = sqliteHelper.getIntook(dateNow)
setWaterLevel(inTook, totalIntake)
}
override fun onStart() {
super.onStart()
val outValue = TypedValue()
applicationContext.theme.resolveAttribute(
android.R.attr.selectableItemBackground,
outValue,
true
)
notificStatus = sharedPref.getBoolean(AppUtils.NOTIFICATION_STATUS_KEY, true)
val alarm = AlarmHelper()
if (!alarm.checkAlarm(this) && notificStatus) {
btnNotific.setImageDrawable(getDrawable(R.drawable.ic_bell))
alarm.setAlarm(
this,
sharedPref.getInt(AppUtils.NOTIFICATION_FREQUENCY_KEY, 30).toLong()
)
}
if (notificStatus) {
btnNotific.setImageDrawable(getDrawable(R.drawable.ic_bell))
} else {
btnNotific.setImageDrawable(getDrawable(R.drawable.ic_bell_disabled))
}
sqliteHelper.addAll(dateNow, 0, totalIntake)
updateValues()
btnMenu.setOnClickListener {
val bottomSheetFragment =
BottomSheetFragment(
this
)
bottomSheetFragment.show(supportFragmentManager, bottomSheetFragment.tag)
AdsUtility.showIntestitialAds()
}
fabAdd.setOnClickListener {
if (selectedOption != null) {
if ((inTook * 100 / totalIntake) <= 140) {
if (sqliteHelper.addIntook(dateNow, selectedOption!!) > 0) {
inTook += selectedOption!!
setWaterLevel(inTook, totalIntake)
Snackbar.make(it, "Your water intake was saved...!!", Snackbar.LENGTH_SHORT)
.show()
}
} else {
Snackbar.make(it, "You already achieved the goal", Snackbar.LENGTH_SHORT).show()
}
selectedOption = null
tvCustom.text = "Custom"
op50ml.background = getDrawable(outValue.resourceId)
op100ml.background = getDrawable(outValue.resourceId)
op150ml.background = getDrawable(outValue.resourceId)
op200ml.background = getDrawable(outValue.resourceId)
op250ml.background = getDrawable(outValue.resourceId)
opCustom.background = getDrawable(outValue.resourceId)
} else {
YoYo.with(Techniques.Shake)
.duration(700)
.playOn(cardView)
Snackbar.make(it, "Please select an option", Snackbar.LENGTH_SHORT).show()
}
}
btnNotific.setOnClickListener {
notificStatus = !notificStatus
sharedPref.edit().putBoolean(AppUtils.NOTIFICATION_STATUS_KEY, notificStatus).apply()
if (notificStatus) {
btnNotific.setImageDrawable(getDrawable(R.drawable.ic_bell))
Snackbar.make(it, "Notification Enabled..", Snackbar.LENGTH_SHORT).show()
alarm.setAlarm(
this,
sharedPref.getInt(AppUtils.NOTIFICATION_FREQUENCY_KEY, 30).toLong()
)
} else {
btnNotific.setImageDrawable(getDrawable(R.drawable.ic_bell_disabled))
Snackbar.make(it, "Notification Disabled..", Snackbar.LENGTH_SHORT).show()
alarm.cancelAlarm(this)
}
}
btnStats.setOnClickListener {
startActivity(Intent(this, StatsActivity::class.java))
AdsUtility.showIntestitialAds()
}
op50ml.setOnClickListener {
if (snackbar != null) {
snackbar?.dismiss()
}
selectedOption = 50
op50ml.background = getDrawable(R.drawable.option_select_bg)
op100ml.background = getDrawable(outValue.resourceId)
op150ml.background = getDrawable(outValue.resourceId)
op200ml.background = getDrawable(outValue.resourceId)
op250ml.background = getDrawable(outValue.resourceId)
opCustom.background = getDrawable(outValue.resourceId)
}
op100ml.setOnClickListener {
if (snackbar != null) {
snackbar?.dismiss()
}
selectedOption = 100
op50ml.background = getDrawable(outValue.resourceId)
op100ml.background = getDrawable(R.drawable.option_select_bg)
op150ml.background = getDrawable(outValue.resourceId)
op200ml.background = getDrawable(outValue.resourceId)
op250ml.background = getDrawable(outValue.resourceId)
opCustom.background = getDrawable(outValue.resourceId)
}
op150ml.setOnClickListener {
if (snackbar != null) {
snackbar?.dismiss()
}
selectedOption = 150
op50ml.background = getDrawable(outValue.resourceId)
op100ml.background = getDrawable(outValue.resourceId)
op150ml.background = getDrawable(R.drawable.option_select_bg)
op200ml.background = getDrawable(outValue.resourceId)
op250ml.background = getDrawable(outValue.resourceId)
opCustom.background = getDrawable(outValue.resourceId)
}
op200ml.setOnClickListener {
if (snackbar != null) {
snackbar?.dismiss()
}
selectedOption = 200
op50ml.background = getDrawable(outValue.resourceId)
op100ml.background = getDrawable(outValue.resourceId)
op150ml.background = getDrawable(outValue.resourceId)
op200ml.background = getDrawable(R.drawable.option_select_bg)
op250ml.background = getDrawable(outValue.resourceId)
opCustom.background = getDrawable(outValue.resourceId)
}
op250ml.setOnClickListener {
if (snackbar != null) {
snackbar?.dismiss()
}
selectedOption = 250
op50ml.background = getDrawable(outValue.resourceId)
op100ml.background = getDrawable(outValue.resourceId)
op150ml.background = getDrawable(outValue.resourceId)
op200ml.background = getDrawable(outValue.resourceId)
op250ml.background = getDrawable(R.drawable.option_select_bg)
opCustom.background = getDrawable(outValue.resourceId)
}
opCustom.setOnClickListener {
if (snackbar != null) {
snackbar?.dismiss()
}
val li = LayoutInflater.from(this)
val promptsView = li.inflate(R.layout.custom_input_dialog, null)
val alertDialogBuilder = AlertDialog.Builder(this)
alertDialogBuilder.setView(promptsView)
val userInput = promptsView
.findViewById(R.id.etCustomInput) as TextInputLayout
alertDialogBuilder.setPositiveButton("OK") { dialog, id ->
val inputText = userInput.editText!!.text.toString()
if (!TextUtils.isEmpty(inputText)) {
tvCustom.text = "${inputText} ml"
selectedOption = inputText.toInt()
}
}.setNegativeButton("Cancel") { dialog, id ->
dialog.cancel()
}
val alertDialog = alertDialogBuilder.create()
alertDialog.show()
op50ml.background = getDrawable(outValue.resourceId)
op100ml.background = getDrawable(outValue.resourceId)
op150ml.background = getDrawable(outValue.resourceId)
op200ml.background = getDrawable(outValue.resourceId)
op250ml.background = getDrawable(outValue.resourceId)
opCustom.background = getDrawable(R.drawable.option_select_bg)
}
}
private fun setWaterLevel(inTook: Int, totalIntake: Int) {
YoYo.with(Techniques.SlideInDown)
.duration(500)
.playOn(tvIntook)
tvIntook.text = "$inTook"
tvTotalIntake.text = "/$totalIntake ml"
val progress = ((inTook / totalIntake.toFloat()) * 100).toInt()
YoYo.with(Techniques.Pulse)
.duration(500)
.playOn(intakeProgress)
intakeProgress.currentProgress = progress
if ((inTook * 100 / totalIntake) > 140) {
Snackbar.make(main_activity_parent, "You achieved the goal", Snackbar.LENGTH_SHORT)
.show()
}
}
override fun onBackPressed() {
if (doubleBackToExitPressedOnce) {
super.onBackPressed()
return
}
this.doubleBackToExitPressedOnce = true
Snackbar.make(
this.window.decorView.findViewById(android.R.id.content),
"Please click BACK again to exit",
Snackbar.LENGTH_SHORT
).show()
Handler().postDelayed({ doubleBackToExitPressedOnce = false }, 1000)
}
}
activity_main.xml :
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_activity_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/ic_app_bg"
tools:context="com.reflection.water.time.MainActivity">
<ImageView
android:id="@+id/btnMenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:background="?attr/selectableItemBackground"
android:padding="4dp"
android:src="@drawable/ic_settings"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
build.gradle :
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
defaultConfig {
applicationId "com.reflection.water.time"
minSdkVersion 21
targetSdkVersion 29
versionCode 5
versionName "1.3"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}