Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: armancodv/integral
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v4.8.0
Choose a base ref
...
head repository: armancodv/integral
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
  • 12 commits
  • 587 files changed
  • 1 contributor

Commits on Jun 12, 2021

  1. profile

    armancodv committed Jun 12, 2021

    Verified

    This commit was signed with the committer’s verified signature.
    armancodv Arman Kolahan
    Copy the full SHA
    a7ea1c6 View commit details
  2. Merge pull request #13 from armancodv/profile

    profile
    armancodv authored Jun 12, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    882032c View commit details
  3. v4.9.0

    armancodv committed Jun 12, 2021

    Verified

    This commit was signed with the committer’s verified signature.
    armancodv Arman Kolahan
    Copy the full SHA
    6cc3cc8 View commit details
  4. update screenshots

    armancodv committed Jun 12, 2021

    Verified

    This commit was signed with the committer’s verified signature.
    armancodv Arman Kolahan
    Copy the full SHA
    d0a12d6 View commit details

Commits on Jun 13, 2021

  1. messaging

    armancodv committed Jun 13, 2021

    Verified

    This commit was signed with the committer’s verified signature.
    armancodv Arman Kolahan
    Copy the full SHA
    a3c63b7 View commit details
  2. Merge pull request #14 from armancodv/messaging

    messaging
    armancodv authored Jun 13, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    7f701f0 View commit details
  3. v4.10.0

    armancodv committed Jun 13, 2021

    Verified

    This commit was signed with the committer’s verified signature.
    armancodv Arman Kolahan
    Copy the full SHA
    e073558 View commit details

Commits on Jun 25, 2021

  1. trigonometry

    armancodv committed Jun 25, 2021

    Verified

    This commit was signed with the committer’s verified signature.
    armancodv Arman Kolahan
    Copy the full SHA
    0a1118c View commit details
  2. Merge pull request #15 from armancodv/trigonometry

    trigonometry
    armancodv authored Jun 25, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    4382352 View commit details
  3. v5.0.0

    armancodv committed Jun 25, 2021

    Verified

    This commit was signed with the committer’s verified signature.
    armancodv Arman Kolahan
    Copy the full SHA
    5a2c47f View commit details

Commits on Jan 2, 2022

  1. github action

    armancodv committed Jan 2, 2022
    Copy the full SHA
    f229ad3 View commit details
  2. Merge pull request #17 from armancodv/github-action

    github action
    armancodv authored Jan 2, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    7cfed1b View commit details
Showing 587 changed files with 4,598 additions and 852 deletions.
47 changes: 47 additions & 0 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Android CI

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: set up JDK 11
uses: actions/setup-java@v2
with:
java-version: '11'
distribution: 'adopt'
cache: gradle

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Find and Replace APP_CONSTANTS_DATASTORE
uses: jacobtomlinson/gha-find-replace@v2
with:
find: "APP_CONSTANTS_DATASTORE"
replace: ${{ secrets.APP_CONSTANTS_DATASTORE }}
include: "**AppConstants.kt"

- name: Find and Replace APP_CONSTANTS_ENCRYPT_PASSWORD
uses: jacobtomlinson/gha-find-replace@v2
with:
find: "APP_CONSTANTS_ENCRYPT_PASSWORD"
replace: ${{ secrets.APP_CONSTANTS_ENCRYPT_PASSWORD }}
include: "**AppConstants.kt"

- name: Provide Google Services JSON
env:
GOOGLE_SERVICES_JSON: ${{ secrets.GOOGLE_SERVICES_JSON }}
run: echo $GOOGLE_SERVICES_JSON > ./app/google-services.json

- name: Build with Gradle
run: ./gradlew build
43 changes: 40 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -17,8 +17,8 @@ android {
applicationId "com.armanco.integral"
minSdkVersion 21
targetSdkVersion 30
versionCode 100015
versionName "4.8.0"
versionCode 100021
versionName "5.0.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@@ -39,6 +39,9 @@ android {
buildFeatures {
viewBinding true
}
lintOptions {
abortOnError false
}

flavorDimensions 'default'
productFlavors {
@@ -54,13 +57,21 @@ android {
dimension 'default'
applicationId "com.armanco.integral_persian"
}
trigonometry {
dimension 'default'
applicationId "com.armanco.trigonometry"
}
trigonometryPersian {
dimension 'default'
applicationId "com.armanco.trigonometry_persian"
}
}

}

dependencies {

implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
@@ -101,13 +112,39 @@ dependencies {
implementation 'com.google.firebase:firebase-analytics-ktx'
implementation 'com.google.firebase:firebase-perf-ktx'
implementation 'com.google.firebase:firebase-config-ktx'
implementation 'com.google.firebase:firebase-auth-ktx'
implementation 'com.google.firebase:firebase-firestore-ktx'
implementation 'com.google.firebase:firebase-messaging-ktx'
implementation 'com.google.firebase:firebase-inappmessaging-display-ktx'

// json
implementation 'com.google.code.gson:gson:2.8.6'

// plot
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'

// auth
implementation 'com.google.android.gms:play-services-auth:19.0.0'
implementation 'com.firebaseui:firebase-ui-auth:7.1.1'

// encrypt
implementation 'com.scottyab:aescrypt:0.0.1'

// datastore
implementation("androidx.datastore:datastore-preferences:1.0.0-beta01")

// coroutines
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3")
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.4.3'

// glide
implementation 'com.github.bumptech.glide:glide:4.12.0'
kapt 'com.github.bumptech.glide:compiler:4.12.0'

// blur
implementation 'jp.wasabeef:blurry:4.0.0'


testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test:rules:1.3.0'
68 changes: 36 additions & 32 deletions app/src/androidTest/java/com/armanco/integral/Screenshots.kt
Original file line number Diff line number Diff line change
@@ -4,17 +4,20 @@ import android.view.View
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
import androidx.navigation.Navigation
import androidx.navigation.findNavController
import androidx.recyclerview.widget.RecyclerView
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.contrib.RecyclerViewActions
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.isRoot
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
import androidx.test.rule.ActivityTestRule
import androidx.viewpager2.widget.ViewPager2
import com.armanco.integral.ui.activity.main.MainActivity
import org.hamcrest.Matcher
import org.junit.AfterClass
import org.junit.BeforeClass
@@ -37,27 +40,46 @@ class Screenshots {

@Test
fun testTakeScreenshot() {

// category
runOnUiThread { AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_NO) }
onView(isRoot()).perform(waitFor(2000))
Screengrab.screenshot("category_light")
onView(isRoot()).perform(waitFor(1000))
Screengrab.screenshot("3_category_light")
runOnUiThread { AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_YES) }
onView(isRoot()).perform(waitFor(2000))
Screengrab.screenshot("category_dark")
onView(isRoot()).perform(waitFor(1000))
Screengrab.screenshot("3_category_dark")

// formula
onView(withId(R.id.rvCategory)).perform(
RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(2,click()))
runOnUiThread { AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_NO) }
onView(isRoot()).perform(waitFor(2000))
Screengrab.screenshot("formula_light")
onView(isRoot()).perform(waitFor(1000))
Screengrab.screenshot("4_formula_light")
runOnUiThread { AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_YES) }
onView(isRoot()).perform(waitFor(2000))
Screengrab.screenshot("formula_dark")
onView(withId(R.id.solver)).perform(click())
onView(isRoot()).perform(waitFor(1000))
Screengrab.screenshot("4_formula_dark")

// solver
runOnUiThread { activityRule.activity.findNavController(R.id.nav_host_fragment).navigate(R.id.solverFragment) }
runOnUiThread { AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_NO) }
onView(isRoot()).perform(waitFor(2000))
Screengrab.screenshot("solver_light")
onView(isRoot()).perform(waitFor(1000))
Screengrab.screenshot("1_solver_light")
runOnUiThread { AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_YES) }
onView(isRoot()).perform(waitFor(2000))
Screengrab.screenshot("solver_dark")
onView(isRoot()).perform(waitFor(1000))
Screengrab.screenshot("1_solver_dark")

// plot
runOnUiThread {
activityRule.activity.findNavController(R.id.nav_host_fragment).navigate(R.id.plotFragment)
}
onView(isRoot()).perform(waitFor(1000))
onView(withId(R.id.plotButton))?.perform(click())
runOnUiThread { AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_NO) }
onView(isRoot()).perform(waitFor(1000))
Screengrab.screenshot("2_plot_light")
runOnUiThread { AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_YES) }
onView(isRoot()).perform(waitFor(1000))
Screengrab.screenshot("2_plot_dark")

}

@@ -71,24 +93,6 @@ class Screenshots {
}
}


fun clickChildViewWithId(id: Int): ViewAction {
return object : ViewAction {
override fun getConstraints(): Matcher<View>? {
return null
}

override fun getDescription(): String {
return "Click on a child view with specified id."
}

override fun perform(uiController: UiController, view: View) {
val v = view.findViewById<View>(id)
v.performClick()
}
}
}

companion object {

@BeforeClass @JvmStatic
23 changes: 22 additions & 1 deletion app/src/debug/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -20,17 +20,38 @@
android:theme="@style/Theme.Integral">


<activity android:name=".MainActivity">
<activity android:name=".ui.activity.splash.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity android:name=".ui.activity.main.MainActivity" />

<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-4301546764905932~1198504240"/>

<service
android:name=".service.IntegralMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>

<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_icon" />

<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/primary" />

<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/default_notification_channel_id" />
</application>

</manifest>
23 changes: 22 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -14,17 +14,38 @@
android:theme="@style/Theme.Integral">


<activity android:name=".MainActivity">
<activity android:name=".ui.activity.splash.SplashActivity" android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity android:name=".ui.activity.main.MainActivity" />

<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-4301546764905932~1198504240"/>

<service
android:name=".service.IntegralMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>

<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_icon" />

<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/primary" />

<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/default_notification_channel_id" />
</application>

</manifest>
Binary file modified app/src/main/ic_launcher-playstore.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions app/src/main/java/com/armanco/integral/AppConstants.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.armanco.integral

object AppConstants {
const val ENCRYPT_PASSWORD = "APP_CONSTANTS_ENCRYPT_PASSWORD" // REPLACE
const val DATASTORE = "APP_CONSTANTS_DATASTORE" // REPLACE
const val FLAVOR_PERSIAN = "persian"
const val FLAVOR_FREE = "free"
const val FLAVOR_PRO = "pro"
const val FLAVOR_TRIGONOMETRY = "trigonometry"
const val FLAVOR_TRIGONOMETRY_PERSIAN = "trigonometryPersian"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.armanco.integral.data.datastore

import android.content.Context
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import com.armanco.integral.AppConstants.ENCRYPT_PASSWORD
import com.armanco.integral.utils.extensions.dataStore
import com.scottyab.aescrypt.AESCrypt
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import java.lang.Exception

class DataStoreManager(appContext: Context) {

private val dataStore = appContext.dataStore

suspend fun setXp(value: Int?) {
setEncrypt(XP, value)
}

val xp: Flow<Int?> = getDecryptInt(XP)

suspend fun setCounterPlot(value: Int?) {
setEncrypt(COUNTER_PLOT, value)
}

val counterPlot: Flow<Int?> = getDecryptInt(COUNTER_PLOT)

suspend fun setCounterCalculate(value: Int?) {
setEncrypt(COUNTER_CALCULATE, value)
}

val counterCalculate: Flow<Int?> = getDecryptInt(COUNTER_CALCULATE)

suspend fun setCounterFormulaCounter(value: Int?) {
setEncrypt(COUNTER_FORMULA, value)
}

val counterFormula: Flow<Int?> = getDecryptInt(COUNTER_FORMULA)

suspend fun setCounterAd(value: Int?) {
setEncrypt(COUNTER_AD, value)
}

val counterAd: Flow<Int?> = getDecryptInt(COUNTER_AD)

private suspend fun setEncrypt(key: String, value: Any?) {
try {
dataStore.edit { preferences ->
if (value != null && value != "") {
preferences[stringPreferencesKey(key)] =
AESCrypt.encrypt(ENCRYPT_PASSWORD, value.toString())
} else {
preferences.remove(stringPreferencesKey(key))
}
}
} catch (ignore: Exception) {
}
}

private fun getDecryptInt(key: String): Flow<Int?> {
return dataStore.data.map { preferences ->
if (!preferences[stringPreferencesKey(key)].isNullOrBlank()) {
AESCrypt.decrypt(ENCRYPT_PASSWORD, preferences[stringPreferencesKey(key)])
.toIntOrNull()
} else null
}
}

companion object {
const val XP = "xp"
const val COUNTER_PLOT = "counterPlot"
const val COUNTER_CALCULATE = "counterCalculate"
const val COUNTER_FORMULA = "counterFormula"
const val COUNTER_AD = "counterAd"
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.armanco.integral.managers.db
package com.armanco.integral.data.db

import androidx.room.*
import com.armanco.integral.models.Category
import com.armanco.integral.data.models.Category
import com.armanco.integral.data.models.Section

@Dao
interface CategoryDao {
@Query("SELECT * FROM category")
suspend fun getAll(): List<Category>

@Query("SELECT * FROM category WHERE section = :section")
suspend fun getBySection(section: Section): List<Category>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(vararg category: Category)

Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.armanco.integral.managers.db
package com.armanco.integral.data.db

import androidx.room.Database
import androidx.room.RoomDatabase
import com.armanco.integral.models.Category
import com.armanco.integral.models.Formula
import androidx.room.TypeConverters
import com.armanco.integral.data.models.Category
import com.armanco.integral.data.models.Formula

@Database(entities = [Category::class, Formula::class], version = 1)
@Database(entities = [Category::class, Formula::class], version = 2)
@TypeConverters(SectionTypeConverter::class)
abstract class Db : RoomDatabase() {
abstract fun categoryDao(): CategoryDao
abstract fun formulaDao(): FormulaDao
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.armanco.integral.managers.db
package com.armanco.integral.data.db

import androidx.room.*
import com.armanco.integral.models.Formula
import com.armanco.integral.data.models.Formula

@Dao
interface FormulaDao {
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.armanco.integral.data.db

import androidx.room.TypeConverter
import com.armanco.integral.data.models.Section


class SectionTypeConverter {

@TypeConverter
fun toSection(value: Int) = enumValues<Section>()[value]

@TypeConverter
fun fromSection(value: Section) = value.ordinal

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.armanco.integral.data.models

enum class AngleUnit {
DEGREE,
RADIAN
}
17 changes: 17 additions & 0 deletions app/src/main/java/com/armanco/integral/data/models/Category.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.armanco.integral.data.models

import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.TypeConverters
import com.armanco.integral.data.db.SectionTypeConverter

@Entity
data class Category(
@PrimaryKey val id: Int,
@ColumnInfo(name = "drawable_res") @DrawableRes val drawableRes: Int,
@ColumnInfo(name = "string_res") @StringRes val stringRes: Int,
@ColumnInfo(name = "section") val section: Section?,
)
15 changes: 15 additions & 0 deletions app/src/main/java/com/armanco/integral/data/models/ConfigAds.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.armanco.integral.data.models

data class ConfigAds (
val bannerId: String? = null,
val interstitialId: String? = null,
val randomSize: Int? = null,
val integral: ConfigAdsApp? = null,
val integralPersian: ConfigAdsApp? = null,
val trigonometry: ConfigAdsApp? = null,
val trigonometryPersian: ConfigAdsApp? = null,
) {
companion object {
const val KEY = "ads"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.armanco.integral.data.models

data class ConfigAdsApp(
val appId: String,
val bannerId: String? = null,
val rewardedId: String? = null,
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.armanco.integral.models
package com.armanco.integral.data.models

data class ConfigLinks (
val proPackageName: String? = null,
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.armanco.integral.models
package com.armanco.integral.data.models

import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
5 changes: 5 additions & 0 deletions app/src/main/java/com/armanco/integral/data/models/Keys.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.armanco.integral.data.models

object Keys {
const val SECTION = "section"
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.armanco.integral.models
package com.armanco.integral.data.models

import com.github.mikephil.charting.data.Entry

6 changes: 6 additions & 0 deletions app/src/main/java/com/armanco/integral/data/models/Section.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.armanco.integral.data.models

enum class Section {
INTEGRAL,
TRIGONOMETRY
}
7 changes: 7 additions & 0 deletions app/src/main/java/com/armanco/integral/data/models/Solver.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.armanco.integral.data.models

enum class Solver {
INTEGRAL,
TRIGONOMETRY,
INVERSE_TRIGONOMETRY
}
61 changes: 61 additions & 0 deletions app/src/main/java/com/armanco/integral/data/models/User.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.armanco.integral.data.models

import com.google.firebase.auth.FirebaseUser

data class User(
val userId: String? = null,
var name: String? = null,
var email: String? = null,
var photoUrl: String? = null,
val emailVerified: Boolean? = null,
var phoneNumber: String? = null,
var xp: Int? = null,
var counterPlot: Int? = null,
var counterCalculate: Int? = null,
var counterFormula: Int? = null,
var counterAd: Int? = null,
var messagingToken: String? = null,
) {
fun toMapRemoveNull(): Map<String, Any> {
val hashMap = hashMapOf<String, Any>()
userId?.let { hashMap[USER_ID] = it }
name?.let { hashMap[NAME] = it }
email?.let { hashMap[EMAIL] = it }
photoUrl?.let { hashMap[PHOTO_URL] = it }
emailVerified?.let { hashMap[EMAIL_VERIFIED] = it }
phoneNumber?.let { hashMap[PHONE_NUMBER] = it }
xp?.let { if(it>0) hashMap[XP] = it }
counterPlot?.let { if(it>0) hashMap[COUNTER_PLOT] = it }
counterCalculate?.let { if(it>0) hashMap[COUNTER_CALCULATE] = it }
counterFormula?.let { if(it>0) hashMap[COUNTER_FORMULA] = it }
counterAd?.let { if(it>0) hashMap[COUNTER_AD] = it }
messagingToken?.let { hashMap[MESSAGING_TOKEN] = it }
return hashMap
}

companion object {
const val USER_ID = "userId"
const val NAME = "name"
const val EMAIL = "email"
const val PHOTO_URL = "photoUrl"
const val EMAIL_VERIFIED = "emailVerified"
const val PHONE_NUMBER = "phoneNumber"
const val XP = "xp"
const val COUNTER_PLOT = "counterPlot"
const val COUNTER_CALCULATE = "counterCalculate"
const val COUNTER_FORMULA = "counterFormula"
const val COUNTER_AD = "counterAd"
const val MESSAGING_TOKEN = "messagingToken"

fun fromFirebaseUser(firebaseUser: FirebaseUser): User {
return User(
userId = firebaseUser.uid,
name = firebaseUser.displayName,
email = firebaseUser.email,
photoUrl = firebaseUser.photoUrl?.toString(),
emailVerified = firebaseUser.isEmailVerified,
phoneNumber = firebaseUser.phoneNumber
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.armanco.integral.data.repository

import com.armanco.integral.R
import com.armanco.integral.data.db.CategoryDao
import com.armanco.integral.data.models.Category
import com.armanco.integral.data.models.Section
import javax.inject.Inject

class CategoryRepository @Inject constructor(
private val categoryDao: CategoryDao
){
suspend fun getAll() = categoryDao.getAll()
suspend fun getBySection(section: Section) = categoryDao.getBySection(section)
suspend fun getOne(id: Int) = categoryDao.getOne(id)
suspend fun getCount() = categoryDao.getCount()
suspend fun deleteAll() = categoryDao.deleteAll()
suspend fun insertAll(vararg category: Category) = categoryDao.insertAll(*category)
suspend fun populate() {
deleteAll()
insertAll(
Category(1, R.drawable.ic_cat_1, R.string.category_1, Section.INTEGRAL),
Category(2, R.drawable.ic_cat_2, R.string.category_2, Section.INTEGRAL),
Category(3, R.drawable.ic_cat_3, R.string.category_3, Section.INTEGRAL),
Category(4, R.drawable.ic_cat_4, R.string.category_4, Section.INTEGRAL),
Category(5, R.drawable.ic_cat_5, R.string.category_5, Section.INTEGRAL),
Category(6, R.drawable.ic_cat_6, R.string.category_6, Section.INTEGRAL),
Category(7, R.drawable.ic_cat_7, R.string.category_7, Section.INTEGRAL),
Category(8, R.drawable.ic_cat_8, R.string.category_8, Section.INTEGRAL),
Category(9, R.drawable.ic_cat_9, R.string.category_9, Section.INTEGRAL),
Category(10, R.drawable.ic_cat_10, R.string.category_10, Section.INTEGRAL),
Category(11, R.drawable.ic_cat_11, R.string.category_11, Section.INTEGRAL),

Category(12, R.drawable.ic_formula, R.string.category_tri_1, Section.TRIGONOMETRY),
Category(13, R.drawable.ic_formula, R.string.category_tri_2, Section.TRIGONOMETRY),
Category(14, R.drawable.ic_formula, R.string.category_tri_3, Section.TRIGONOMETRY),
Category(15, R.drawable.ic_formula, R.string.category_tri_4, Section.TRIGONOMETRY),
Category(16, R.drawable.ic_formula, R.string.category_tri_5, Section.TRIGONOMETRY),
Category(17, R.drawable.ic_formula, R.string.category_tri_6, Section.TRIGONOMETRY),
Category(18, R.drawable.ic_formula, R.string.category_tri_7, Section.TRIGONOMETRY),
Category(19, R.drawable.ic_formula, R.string.category_tri_8, Section.TRIGONOMETRY),
Category(20, R.drawable.ic_formula, R.string.category_tri_9, Section.TRIGONOMETRY),
Category(21, R.drawable.ic_formula, R.string.category_tri_10, Section.TRIGONOMETRY),
Category(22, R.drawable.ic_formula, R.string.category_tri_11, Section.TRIGONOMETRY),
Category(23, R.drawable.ic_formula, R.string.category_tri_12, Section.TRIGONOMETRY),
)
}

}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.armanco.integral.managers.repository
package com.armanco.integral.data.repository

import com.armanco.integral.R
import com.armanco.integral.managers.db.FormulaDao
import com.armanco.integral.models.Formula
import com.armanco.integral.data.db.FormulaDao
import com.armanco.integral.data.models.Formula
import javax.inject.Inject

class FormulaRepository @Inject constructor(
private val formulaDao: FormulaDao
){
) {
suspend fun getAll() = formulaDao.getAll()
suspend fun getCount() = formulaDao.getCount()
suspend fun deleteAll() = formulaDao.deleteAll()
@@ -251,5 +251,100 @@ class FormulaRepository @Inject constructor(
Formula(1125, 11, R.drawable.img_11_25, R.string.formula_11_25),
Formula(1126, 11, R.drawable.img_11_26, R.string.formula_11_26),
)
insertAll(
Formula(1201, 12, R.drawable.tri_1_1, R.string.tri_1_1),
Formula(1202, 12, R.drawable.tri_1_2, R.string.tri_1_2),
Formula(1203, 12, R.drawable.tri_1_3, R.string.tri_1_3),
Formula(1204, 12, R.drawable.tri_1_4, R.string.tri_1_4),
Formula(1205, 12, R.drawable.tri_1_5, R.string.tri_1_5),
Formula(1206, 12, R.drawable.tri_1_6, R.string.tri_1_6),
Formula(1207, 12, R.drawable.tri_1_7, R.string.tri_1_7),

Formula(1301, 13, R.drawable.tri_2_1, R.string.tri_2_1),
Formula(1302, 13, R.drawable.tri_2_2, R.string.tri_2_2),
Formula(1303, 13, R.drawable.tri_2_3, R.string.tri_2_3),
Formula(1304, 13, R.drawable.tri_2_4, R.string.tri_2_4),
Formula(1305, 13, R.drawable.tri_2_5, R.string.tri_2_5),
Formula(1306, 13, R.drawable.tri_2_6, R.string.tri_2_6),

Formula(1401, 14, R.drawable.tri_3_1, R.string.tri_3_1),
Formula(1402, 14, R.drawable.tri_3_2, R.string.tri_3_2),
Formula(1403, 14, R.drawable.tri_3_3, R.string.tri_3_3),
Formula(1404, 14, R.drawable.tri_3_4, R.string.tri_3_4),
Formula(1405, 14, R.drawable.tri_3_5, R.string.tri_3_5),
Formula(1406, 14, R.drawable.tri_3_6, R.string.tri_3_6),

Formula(1501, 15, R.drawable.tri_4_1, R.string.tri_4_1),
Formula(1502, 15, R.drawable.tri_4_2, R.string.tri_4_2),
Formula(1503, 15, R.drawable.tri_4_3, R.string.tri_4_3),
Formula(1504, 15, R.drawable.tri_4_4, R.string.tri_4_4),

Formula(1601, 16, R.drawable.tri_5_1, R.string.tri_5_1),
Formula(1602, 16, R.drawable.tri_5_2, R.string.tri_5_2),
Formula(1603, 16, R.drawable.tri_5_3, R.string.tri_5_3),
Formula(1604, 16, R.drawable.tri_5_4, R.string.tri_5_4),

Formula(1701, 17, R.drawable.tri_6_1, R.string.tri_6_1),
Formula(1702, 17, R.drawable.tri_6_2, R.string.tri_6_2),
Formula(1703, 17, R.drawable.tri_6_3, R.string.tri_6_3),
Formula(1704, 17, R.drawable.tri_6_4, R.string.tri_6_4),

Formula(1801, 18, R.drawable.tri_7_1, R.string.tri_7_1),
Formula(1802, 18, R.drawable.tri_7_2, R.string.tri_7_2),
Formula(1803, 18, R.drawable.tri_7_3, R.string.tri_7_3),
Formula(1804, 18, R.drawable.tri_7_4, R.string.tri_7_4),
Formula(1805, 18, R.drawable.tri_7_5, R.string.tri_7_5),
Formula(1806, 18, R.drawable.tri_7_6, R.string.tri_7_6),

Formula(1901, 19, R.drawable.tri_8_1, R.string.tri_8_1),
Formula(1902, 19, R.drawable.tri_8_2, R.string.tri_8_2),
Formula(1903, 19, R.drawable.tri_8_3, R.string.tri_8_3),
Formula(1904, 19, R.drawable.tri_8_4, R.string.tri_8_4),
Formula(1905, 19, R.drawable.tri_8_5, R.string.tri_8_5),

Formula(2001, 20, R.drawable.tri_9_1, R.string.tri_9_1),
Formula(2002, 20, R.drawable.tri_9_2, R.string.tri_9_2),
Formula(2003, 20, R.drawable.tri_9_3, R.string.tri_9_3),

Formula(2101, 21, R.drawable.tri_10_1, R.string.tri_10_1),
Formula(2102, 21, R.drawable.tri_10_2, R.string.tri_10_2),
Formula(2103, 21, R.drawable.tri_10_3, R.string.tri_10_3),
Formula(2104, 21, R.drawable.tri_10_4, R.string.tri_10_4),
Formula(2105, 21, R.drawable.tri_10_5, R.string.tri_10_5),
Formula(2106, 21, R.drawable.tri_10_6, R.string.tri_10_6),
Formula(2107, 21, R.drawable.tri_10_7, R.string.tri_10_7),
Formula(2108, 21, R.drawable.tri_10_8, R.string.tri_10_8),
Formula(2109, 21, R.drawable.tri_10_9, R.string.tri_10_9),
Formula(2110, 21, R.drawable.tri_10_10, R.string.tri_10_10),
Formula(2111, 21, R.drawable.tri_10_11, R.string.tri_10_11),

Formula(2201, 22, R.drawable.tri_11_1, R.string.tri_11_1),
Formula(2202, 22, R.drawable.tri_11_2, R.string.tri_11_2),
Formula(2203, 22, R.drawable.tri_11_3, R.string.tri_11_3),
Formula(2204, 22, R.drawable.tri_11_4, R.string.tri_11_4),
Formula(2205, 22, R.drawable.tri_11_5, R.string.tri_11_5),
Formula(2206, 22, R.drawable.tri_11_6, R.string.tri_11_6),
Formula(2207, 22, R.drawable.tri_11_7, R.string.tri_11_7),
Formula(2208, 22, R.drawable.tri_11_8, R.string.tri_11_8),
Formula(2209, 22, R.drawable.tri_11_9, R.string.tri_11_9),
Formula(2210, 22, R.drawable.tri_11_10, R.string.tri_11_10),
Formula(2211, 22, R.drawable.tri_11_11, R.string.tri_11_11),
Formula(2212, 22, R.drawable.tri_11_12, R.string.tri_11_12),

Formula(2301, 23, R.drawable.tri_12_1, R.string.tri_12_1),
Formula(2302, 23, R.drawable.tri_12_2, R.string.tri_12_2),
Formula(2303, 23, R.drawable.tri_12_3, R.string.tri_12_3),
Formula(2304, 23, R.drawable.tri_12_4, R.string.tri_12_4),
Formula(2305, 23, R.drawable.tri_12_5, R.string.tri_12_5),
Formula(2306, 23, R.drawable.tri_12_6, R.string.tri_12_6),
Formula(2307, 23, R.drawable.tri_12_7, R.string.tri_12_7),
Formula(2308, 23, R.drawable.tri_12_8, R.string.tri_12_8),
Formula(2309, 23, R.drawable.tri_12_9, R.string.tri_12_9),
Formula(2310, 23, R.drawable.tri_12_10, R.string.tri_12_10),
Formula(2311, 23, R.drawable.tri_12_11, R.string.tri_12_11),
Formula(2312, 23, R.drawable.tri_12_12, R.string.tri_12_12),
Formula(2313, 23, R.drawable.tri_12_13, R.string.tri_12_13)

)
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
package com.armanco.integral.managers.repository
package com.armanco.integral.data.repository

import com.github.mikephil.charting.data.Entry
import com.mathlibrary.exception.CalculatorException
import com.mathlibrary.function.FunctionX
import com.mathlibrary.integral.Integral
import javax.inject.Inject

class PlotRepository @Inject constructor(
private val solverRepository: SolverRepository
) {

@Throws(CalculatorException::class)
fun value(f: String, x: Double): Double {
val function = FunctionX(f, false)
return function.getF_xo(x)
}

@Throws(CalculatorException::class)
fun entries(f: String, lowerLimit: Double, upperLimit: Double, steps: Int): List<Entry> {
val entries = mutableListOf<Entry>()
val dx = (upperLimit - lowerLimit) / steps.toDouble()
@@ -24,6 +26,7 @@ class PlotRepository @Inject constructor(
return entries
}

@Throws(CalculatorException::class)
fun entriesIntegral(f: String, lowerLimit: Double, upperLimit: Double, steps: Int): List<Entry> {
val entries = mutableListOf<Entry>()
val dx = (upperLimit - lowerLimit) / steps.toDouble()
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.armanco.integral.data.repository

import com.mathlibrary.exception.CalculatorException
import com.mathlibrary.integral.Integral
import javax.inject.Inject

class SolverRepository @Inject constructor() {

@Throws(CalculatorException::class)
fun trapezoidal(f: String, lowerLimit: Double, upperLimit: Double): Double? {
val integral = Integral(f, false)
return integral.trapezoidal(lowerLimit, upperLimit)
}

@Throws(CalculatorException::class)
fun simpson(f: String, lowerLimit: Double, upperLimit: Double): Double? {
val integral = Integral(f, false)
return integral.simpson(lowerLimit, upperLimit)
}

@Throws(CalculatorException::class)
fun romberg(f: String, lowerLimit: Double, upperLimit: Double, steps: Int): Double? {
val integral = Integral(f, false)
return integral.Romberg(lowerLimit, upperLimit, steps)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.armanco.integral.data.repository

import com.armanco.integral.data.datastore.DataStoreManager
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import javax.inject.Inject

class StatRepository @Inject constructor(
private val dataStoreManager: DataStoreManager,
) {
suspend fun setXp(value: Int?) {
dataStoreManager.setXp(value)
}

val xp: Flow<Int?> = dataStoreManager.xp

suspend fun increaseXp(increase: Int = 1): Int {
var value = xp.first() ?: 0
value += increase
setXp(value)
return value
}

suspend fun setCounterPlot(value: Int?) {
dataStoreManager.setCounterPlot(value)
}

val counterPlot: Flow<Int?> = dataStoreManager.counterPlot

suspend fun increaseCounterPlot(): Int {
var value = counterPlot.first() ?: 0
value ++
setCounterPlot(value)
return value
}

suspend fun setCounterCalculate(value: Int?) {
dataStoreManager.setCounterCalculate(value)
}

val counterCalculate: Flow<Int?> = dataStoreManager.counterCalculate

suspend fun increaseCounterCalculate(): Int {
var value = counterCalculate.first() ?: 0
value ++
setCounterCalculate(value)
return value
}

suspend fun setCounterFormula(value: Int?) {
dataStoreManager.setCounterFormulaCounter(value)
}

val counterFormula: Flow<Int?> = dataStoreManager.counterFormula

suspend fun increaseCounterFormula(): Int {
var value = counterFormula.first() ?: 0
value ++
setCounterFormula(value)
return value
}

suspend fun setCounterAd(value: Int?) {
dataStoreManager.setCounterAd(value)
}

val counterAd: Flow<Int?> = dataStoreManager.counterAd

suspend fun increaseCounterAd(): Int {
var value = counterAd.first() ?: 0
value ++
setCounterAd(value)
return value
}

companion object {
const val XP_CALCULATE = 3
const val XP_PLOT = 3
const val XP_FORMULA = 2
const val XP_AD = 5
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.armanco.integral.data.repository

import com.armanco.integral.data.models.User
import com.google.android.gms.tasks.Task
import com.google.firebase.firestore.DocumentSnapshot
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.firestore.SetOptions
import javax.inject.Inject

class UserRepository @Inject constructor(
private val firestore: FirebaseFirestore
) {

fun get(userId: String): Task<DocumentSnapshot> {
return firestore.collection(COLLECTION).document(userId)
.get()
}

fun set(user: User): Task<Void>? {
return user.userId?.let {
firestore.collection(COLLECTION).document(it)
.set(user.toMapRemoveNull(), SetOptions.merge())
}
}

fun delete(userId: String): Task<Void> {
return firestore.collection(COLLECTION).document(userId)
.delete()
}

companion object {
const val COLLECTION = "users"
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.armanco.integral
package com.armanco.integral.di

import android.content.Context
import androidx.room.Room
import com.armanco.integral.managers.db.Db
import com.armanco.integral.data.datastore.DataStoreManager
import com.armanco.integral.data.db.Db
import com.armanco.integral.utils.facade.EventFacade
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.firestore.ktx.firestore
import com.google.firebase.ktx.Firebase
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import com.google.firebase.remoteconfig.ktx.remoteConfig
@@ -19,14 +22,25 @@ import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
object AppModule {

@Singleton
@Provides
fun provideFirestore(): FirebaseFirestore {
return Firebase.firestore
}

@Provides
@Singleton
fun dataStoreManager(@ApplicationContext appContext: Context): DataStoreManager =
DataStoreManager(appContext)

@Singleton
@Provides
fun provideDatabase(@ApplicationContext appContext: Context): Db {
return Room.databaseBuilder(
appContext,
Db::class.java,
"database"
).build()
).fallbackToDestructiveMigration().build()
}

@Singleton

This file was deleted.

This file was deleted.

10 changes: 0 additions & 10 deletions app/src/main/java/com/armanco/integral/models/Category.kt

This file was deleted.

10 changes: 0 additions & 10 deletions app/src/main/java/com/armanco/integral/models/ConfigAds.kt

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.armanco.integral.service

import com.google.firebase.messaging.FirebaseMessagingService

class IntegralMessagingService: FirebaseMessagingService() {
override fun onNewToken(token: String) {
super.onNewToken(token)
}
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
package com.armanco.integral
package com.armanco.integral.ui.activity.main

import android.os.Bundle
import android.util.Log
import android.view.MenuItem
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupWithNavController
import com.armanco.integral.AppConstants
import com.armanco.integral.BuildConfig
import com.armanco.integral.R
import com.armanco.integral.utils.extensions.configAds
import com.armanco.integral.utils.extensions.isPersian
import com.armanco.integral.utils.extensions.isIntegralPersianOrTrigonometryPersian
import com.armanco.integral.utils.extensions.isPro
import com.armanco.integral.utils.extensions.setLocale
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.AdSize
import com.google.android.gms.ads.AdView
import com.google.android.gms.ads.MobileAds
import com.google.android.material.bottomnavigation.BottomNavigationView
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.activity_main.*
import java.util.*
@@ -32,8 +30,21 @@ class MainActivity: AppCompatActivity() {
val model: MainViewModel by viewModels()
model.initEvents(this)
model.configAds.observe(this) {
if(!isPro) initAdMob(it?.bannerId)
else removeAdMob()
initAdMob(when (BuildConfig.FLAVOR) {
AppConstants.FLAVOR_PERSIAN -> {
it?.integralPersian?.bannerId
}
AppConstants.FLAVOR_FREE -> {
it?.integral?.bannerId
}
AppConstants.FLAVOR_TRIGONOMETRY -> {
it?.trigonometry?.bannerId
}
AppConstants.FLAVOR_TRIGONOMETRY_PERSIAN -> {
it?.trigonometryPersian?.bannerId
}
else -> null
})
}
model.remoteConfig.fetchAndActivate().addOnCompleteListener(this) {
model.configAds.postValue(model.remoteConfig.configAds)
@@ -43,12 +54,16 @@ class MainActivity: AppCompatActivity() {

override fun onResume() {
super.onResume()
if(isPersian) {
if(isIntegralPersianOrTrigonometryPersian) {
setLocale(Locale("fa"))
}
}

private fun initAdMob(bannerId: String?) {
if(bannerId.isNullOrBlank()) {
removeAdMob()
return
}
MobileAds.initialize(this)
removeAdMob()
val adView = AdView(this)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.armanco.integral
package com.armanco.integral.ui.activity.main

import android.content.Context
import androidx.lifecycle.MutableLiveData
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.armanco.integral.ui.activity.splash

import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import com.armanco.integral.R
import com.armanco.integral.ui.activity.main.MainActivity
import com.armanco.integral.utils.extensions.configAds
import dagger.hilt.android.AndroidEntryPoint


@AndroidEntryPoint
class SplashActivity: AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_splash)
val model: SplashViewModel by viewModels()
model.load()
model.isReady.observe(this) {
if(it) goToMain()
}
}

private fun goToMain() {
val intent = Intent(this, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
finish()
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.armanco.integral.ui.activity.splash

import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.armanco.integral.data.models.User
import com.armanco.integral.data.repository.CategoryRepository
import com.armanco.integral.data.repository.FormulaRepository
import com.armanco.integral.data.repository.StatRepository
import com.armanco.integral.data.repository.UserRepository
import com.armanco.integral.utils.facade.AuthFacade
import com.armanco.integral.utils.facade.InitFacade
import com.google.firebase.auth.FirebaseUser
import com.google.firebase.firestore.ktx.toObject
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.tasks.await
import javax.inject.Inject
import kotlin.math.max

@HiltViewModel
class SplashViewModel @Inject constructor(
private val categoryRepository: CategoryRepository,
private val formulaRepository: FormulaRepository,
private val userRepository: UserRepository,
private val statRepository: StatRepository,
private val initFacade: InitFacade,
): ViewModel() {
val isReady = MutableLiveData(false)

fun load() {
viewModelScope.launch {
populate()
initFacade.init()
isReady.postValue(true)
}
}

private suspend fun populate() {
categoryRepository.populate()
formulaRepository.populate()
}



}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.armanco.integral.view.adapter
package com.armanco.integral.ui.adapter

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.armanco.integral.R
import com.armanco.integral.models.Category
import com.armanco.integral.view.component.CategoryView
import com.armanco.integral.data.models.Category
import com.armanco.integral.ui.component.CategoryView

class CategoryAdapter(var onCardClick: ((Category)->Unit)? = null): RecyclerView.Adapter<CategoryAdapter.ViewHolder>() {

Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.armanco.integral.view.adapter
package com.armanco.integral.ui.adapter

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.armanco.integral.R
import com.armanco.integral.models.Formula
import com.armanco.integral.view.component.FormulaView
import com.armanco.integral.data.models.Formula
import com.armanco.integral.ui.component.FormulaView

class FormulaAdapter(var onCardClick: ((Formula)->Unit)? = null): RecyclerView.Adapter<FormulaAdapter.ViewHolder>() {

Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.armanco.integral.view.component
package com.armanco.integral.ui.component

import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.LinearLayoutManager
import com.armanco.integral.R
import com.armanco.integral.models.Category
import com.armanco.integral.view.adapter.CategoryAdapter
import com.armanco.integral.data.models.Category
import com.armanco.integral.ui.adapter.CategoryAdapter
import kotlinx.android.synthetic.main.view_category_list.view.*
import me.everything.android.ui.overscroll.OverScrollDecoratorHelper

Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.armanco.integral.view.component
package com.armanco.integral.ui.component

import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import com.armanco.integral.R
import com.armanco.integral.models.Category
import com.armanco.integral.data.models.Category
import kotlinx.android.synthetic.main.view_category.view.*

class CategoryView(context: Context, attrs: AttributeSet): ConstraintLayout(context, attrs) {
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.armanco.integral.view.component
package com.armanco.integral.ui.component

import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.LinearLayoutManager
import com.armanco.integral.R
import com.armanco.integral.models.Formula
import com.armanco.integral.view.adapter.FormulaAdapter
import com.armanco.integral.data.models.Formula
import com.armanco.integral.ui.adapter.FormulaAdapter
import kotlinx.android.synthetic.main.view_formula_list.view.*
import me.everything.android.ui.overscroll.OverScrollDecoratorHelper

Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.armanco.integral.view.component
package com.armanco.integral.ui.component

import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import com.armanco.integral.R
import com.armanco.integral.models.Formula
import com.armanco.integral.data.models.Formula
import kotlinx.android.synthetic.main.view_formula.view.*

class FormulaView(context: Context, attrs: AttributeSet): ConstraintLayout(context, attrs) {
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.armanco.integral.ui.component

import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import com.armanco.integral.R
import kotlinx.android.synthetic.main.view_profile_stat.view.*

class ProfileStatView(context: Context, attrs: AttributeSet) : ConstraintLayout(context, attrs) {

var statValue: String?
get() {
return value?.text?.toString()
}
set(value) {
this.value?.text = value
}

init {
LayoutInflater.from(context).inflate(R.layout.view_profile_stat, this, true)
context.theme.obtainStyledAttributes(attrs, R.styleable.ProfileStatView, 0, 0)
.apply {
try {
title?.text = getString(R.styleable.ProfileStatView_profileStatTitle)
image?.setImageDrawable(getDrawable(R.styleable.ProfileStatView_profileStatImage))
value?.text = getString(R.styleable.ProfileStatView_profileStatValue)
} finally {
recycle()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package com.armanco.integral.view.component
package com.armanco.integral.ui.component

import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import com.armanco.integral.R
import kotlinx.android.synthetic.main.view_settings_item.view.*

@@ -19,6 +20,11 @@ class SettingsItemView(context: Context, attrs: AttributeSet): ConstraintLayout(
tvTitle?.text = getString(R.styleable.SettingsItemView_settingsTitle)
ivTitle?.setImageDrawable(getDrawable(R.styleable.SettingsItemView_settingsImage))
ivTitle?.imageTintList = getColorStateList(R.styleable.SettingsItemView_settingsImageTint)
val isRed = getBoolean(R.styleable.SettingsItemView_settingsIsRed, false)
if(isRed) {
ivTitle?.setColorFilter(ContextCompat.getColor(context, R.color.redDark))
setBackgroundColor(ContextCompat.getColor(context, R.color.redBackground))
}
} finally {
recycle()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.armanco.integral.ui.component

import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import com.armanco.integral.R
import kotlinx.android.synthetic.main.view_solver_screen.view.*

class SolverScreenView(context: Context, attrs: AttributeSet) : ConstraintLayout(context, attrs) {

var result: String?
get() {
return screenResult?.text?.toString()
}
set(value) {
screenResult?.text = value
}

init {
LayoutInflater.from(context).inflate(R.layout.view_solver_screen, this, true)
context.theme.obtainStyledAttributes(attrs, R.styleable.SolverScreenView, 0, 0)
.apply {
try {
screenTitle?.text = getString(R.styleable.SolverScreenView_screenTitle)
screenSubtitle?.text = getString(R.styleable.SolverScreenView_screenSubtitle)
screenResult?.text = getString(R.styleable.SolverScreenView_screenResult)
} finally {
recycle()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.armanco.integral.ui.navigation.category

import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.armanco.integral.data.models.Keys
import com.armanco.integral.data.models.Section
import com.armanco.integral.ui.navigation.category.page.CategoryViewPager

class CategoryAdapter(fragment: Fragment, val sections: List<Section>) : FragmentStateAdapter(fragment) {


override fun getItemCount(): Int = sections.size

override fun createFragment(position: Int): Fragment {
return CategoryViewPager().apply { arguments = bundleOf( Keys.SECTION to sections.getOrNull(position)?.ordinal) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.armanco.integral.ui.navigation.category

import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import com.armanco.integral.R
import com.armanco.integral.data.models.Section
import com.armanco.integral.utils.extensions.isTrigonometryOrTrigonometryPersian
import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.fragment_category.*

@AndroidEntryPoint
class CategoryFragment: Fragment(R.layout.fragment_category) {
private val model: CategoryViewModel by activityViewModels()
private var adapter: CategoryAdapter? = null

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val sections = if(isTrigonometryOrTrigonometryPersian) {
listOf( Section.TRIGONOMETRY, Section.INTEGRAL )
} else {
listOf( Section.INTEGRAL, Section.TRIGONOMETRY )
}
adapter = CategoryAdapter(this, sections)
viewPager?.adapter = adapter
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
tab.text = when(sections.getOrNull(position)) {
Section.TRIGONOMETRY -> context?.getString(R.string.trigonometry)
else -> context?.getString(R.string.integral)
}
}.attach()
model.load()
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.armanco.integral.ui.navigation.category

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.armanco.integral.data.repository.CategoryRepository
import com.armanco.integral.data.repository.FormulaRepository
import com.armanco.integral.data.models.Category
import com.armanco.integral.data.models.Section
import com.armanco.integral.ui.navigation.category.CategoryFragment
import com.armanco.integral.utils.facade.EventFacade
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class CategoryViewModel @Inject constructor(
private val repository: CategoryRepository,
private val formulaRepository: FormulaRepository,
private val eventFacade: EventFacade
): ViewModel() {
val categoriesIntegral = MutableLiveData<List<Category>>()
val categoriesTrigonometry = MutableLiveData<List<Category>>()

fun load() {
eventFacade.screenView(CategoryFragment::class.java.simpleName)
}

fun loadSection(section: Section) {
viewModelScope.launch {
when(section) {
Section.INTEGRAL -> categoriesIntegral.postValue(repository.getBySection(section))
Section.TRIGONOMETRY -> categoriesTrigonometry.postValue(repository.getBySection(section))
}
}
}

fun selectCategory(id: Int, title: String) {
eventFacade.selectCategory(id, title)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.armanco.integral.ui.navigation.category.page

import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import com.armanco.integral.R
import com.armanco.integral.data.models.Category
import com.armanco.integral.data.models.Keys
import com.armanco.integral.data.models.Section
import com.armanco.integral.ui.navigation.category.CategoryViewModel
import com.armanco.integral.ui.navigation.formula.FormulaFragment
import kotlinx.android.synthetic.main.view_pager_category.*

class CategoryViewPager: Fragment(R.layout.view_pager_category) {
private val model: CategoryViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val section = enumValues<Section>()[arguments?.getInt(Keys.SECTION, 0) ?: 0]
model.loadSection(section)
categoryListView?.onCardClick = {
navigateToItem(it)
}
when(section) {
Section.INTEGRAL -> model.categoriesIntegral
Section.TRIGONOMETRY -> model.categoriesTrigonometry
}.observe(viewLifecycleOwner) { categories ->
categoryListView?.with(categories)
}
}

private fun navigateToItem(category: Category) {
model.selectCategory(category.id, getString(category.stringRes))
Log.d("category", category.toString())
findNavController().navigate(R.id.action_categoryFragment_to_formulaFragment,
Bundle().apply {
putInt(FormulaFragment.ID_KEY, category.id)
putString(FormulaFragment.TITLE_KEY, getString(category.stringRes))
})
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.armanco.integral.navigation.formula
package com.armanco.integral.ui.navigation.formula

import android.os.Bundle
import android.util.Log
@@ -7,9 +7,9 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import com.armanco.integral.R
import com.armanco.integral.models.Formula
import com.armanco.integral.navigation.image.ImageFragment
import com.armanco.integral.navigation.image.ImageFragment.Companion.IMAGE_KEY
import com.armanco.integral.data.models.Formula
import com.armanco.integral.ui.navigation.image.ImageFragment
import com.armanco.integral.ui.navigation.image.ImageFragment.Companion.IMAGE_KEY
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.fragment_formula.*

Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.armanco.integral.navigation.formula
package com.armanco.integral.ui.navigation.formula

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.armanco.integral.managers.repository.FormulaRepository
import com.armanco.integral.models.Formula
import com.armanco.integral.data.repository.FormulaRepository
import com.armanco.integral.data.models.Formula
import com.armanco.integral.data.repository.StatRepository
import com.armanco.integral.utils.facade.EventFacade
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
@@ -13,6 +14,7 @@ import javax.inject.Inject
@HiltViewModel
class FormulaViewModel @Inject constructor(
private val repository: FormulaRepository,
private val statRepository: StatRepository,
private val eventFacade: EventFacade
): ViewModel() {
val formulas = MutableLiveData<List<Formula>>()
@@ -24,6 +26,10 @@ class FormulaViewModel @Inject constructor(
}

fun selectImage(id: Int, categoryId: Int, title: String) {
eventFacade.selectImage(id, categoryId, title)
viewModelScope.launch {
statRepository.increaseXp(StatRepository.XP_FORMULA)
val counter = statRepository.increaseCounterFormula()
eventFacade.selectImage(id, categoryId, title, counter)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.armanco.integral.ui.navigation.image

import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.armanco.integral.R
import com.armanco.integral.utils.extensions.*
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.fragment_image.*

@AndroidEntryPoint
class ImageFragment: Fragment(R.layout.fragment_image) {
private val model: ImageViewModel by viewModels()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.load()
model.configAds.observe(viewLifecycleOwner) {
activity?.showRewardedRandom(it) {
model.adShown()
}
}
arguments?.getInt(IMAGE_KEY)?.let { photoView?.setImageResource(it) }
}

companion object {
const val IMAGE_KEY = "image"
const val TITLE_KEY = "title"
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
package com.armanco.integral.navigation.image
package com.armanco.integral.ui.navigation.image

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.armanco.integral.data.repository.StatRepository
import com.armanco.integral.data.repository.StatRepository.Companion.XP_AD
import com.armanco.integral.utils.extensions.configAds
import com.armanco.integral.utils.facade.EventFacade
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class ImageViewModel @Inject constructor(
private val eventFacade: EventFacade,
private val remoteConfig: FirebaseRemoteConfig,
private val statRepository: StatRepository
) : ViewModel() {
var configAds = MutableLiveData(remoteConfig.configAds)
fun load() {
eventFacade.screenView(ImageFragment::class.java.simpleName)
}

fun adShown() {
viewModelScope.launch {
statRepository.increaseXp(XP_AD)
statRepository.increaseCounterAd()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.armanco.integral.navigation.plot
package com.armanco.integral.ui.navigation.plot

import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.armanco.integral.navigation.plot.page.PlotChartViewPager
import com.armanco.integral.navigation.plot.page.PlotFormViewPager
import com.armanco.integral.ui.navigation.plot.page.PlotChartViewPager
import com.armanco.integral.ui.navigation.plot.page.PlotFormViewPager


class PlotAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.armanco.integral.navigation.plot
package com.armanco.integral.ui.navigation.plot

import android.os.Bundle
import android.view.View
@@ -28,6 +28,6 @@ class PlotFragment: Fragment(R.layout.fragment_plot) {
}
}.attach()
model.load()
model.plot()
model.plot(track = false)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.armanco.integral.ui.navigation.plot

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.armanco.integral.data.repository.PlotRepository
import com.armanco.integral.data.models.PlotEntries
import com.armanco.integral.data.repository.StatRepository
import com.armanco.integral.utils.extensions.configAds
import com.armanco.integral.utils.facade.EventFacade
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import java.lang.Exception
import javax.inject.Inject

@HiltViewModel
class PlotViewModel @Inject constructor(
private val repository: PlotRepository,
private val statRepository: StatRepository,
private val eventFacade: EventFacade,
private val remoteConfig: FirebaseRemoteConfig,
): ViewModel() {
val configAds = MutableLiveData(remoteConfig.configAds)
val error = MutableLiveData<String?>()
val function = MutableLiveData("sin(x)")
val lowerLimit = MutableLiveData(0.0)
val upperLimit = MutableLiveData(10.0)
val steps = MutableLiveData(100)
val plotEntries = MutableLiveData<PlotEntries>()
val lastFunction = MutableLiveData(function.value)

fun load() {
eventFacade.screenView(PlotFragment::class.java.simpleName)
}

fun plot(track: Boolean = true) {
if(function.value != null && lowerLimit.value!=null && upperLimit.value!=null && steps.value!=null) {
try {
plotEntries.postValue(
PlotEntries(
entries = repository.entries(function.value!!, lowerLimit.value!!, upperLimit.value!!, steps.value!!),
entriesIntegral = repository.entriesIntegral(function.value!!, lowerLimit.value!!, upperLimit.value!!, steps.value!!),
)
)
error.postValue(null)
} catch (e: Exception) {
error.postValue(e.message)
}
if(track) {
viewModelScope.launch {
if(lastFunction.value != function.value) {
lastFunction.postValue(function.value)
statRepository.increaseXp(StatRepository.XP_PLOT)
val counter = statRepository.increaseCounterPlot()
eventFacade.plot(
function = function.value,
lowerLimit = lowerLimit.value,
upperLimit = upperLimit.value,
steps = steps.value,
counter = counter
)
} else {
eventFacade.plot(
function = function.value,
lowerLimit = lowerLimit.value,
upperLimit = upperLimit.value,
steps = steps.value
)
}
}
}
}
}

fun adShown() {
viewModelScope.launch {
statRepository.increaseXp(StatRepository.XP_AD)
statRepository.increaseCounterAd()
}
}

}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.armanco.integral.navigation.plot.page
package com.armanco.integral.ui.navigation.plot.page

import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import com.armanco.integral.R
import com.armanco.integral.navigation.plot.PlotViewModel
import com.armanco.integral.ui.navigation.plot.PlotViewModel
import com.armanco.integral.utils.extensions.plot
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.view_pager_plot_chart.*
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.armanco.integral.navigation.plot.page
package com.armanco.integral.ui.navigation.plot.page

import android.os.Bundle
import android.view.View
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import com.armanco.integral.R
import com.armanco.integral.navigation.plot.PlotViewModel
import com.armanco.integral.ui.navigation.plot.PlotViewModel
import com.armanco.integral.utils.extensions.showRewardedRandom
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.view_pager_plot_form.*

@@ -34,9 +35,15 @@ class PlotFormViewPager: Fragment(R.layout.view_pager_plot_form) {
steps?.addTextChangedListener {
model.steps.postValue(it.toString().toIntOrNull())
}
model.error.observe(viewLifecycleOwner) {
function?.error = if(!it.isNullOrBlank()) it else null
if(it.isNullOrBlank()) onPlot?.invoke()
}
plotButton?.setOnClickListener {
activity?.showRewardedRandom(model.configAds.value) {
model.adShown()
}
model.plot()
onPlot?.invoke()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.armanco.integral.ui.navigation.profile

import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.armanco.integral.R
import com.armanco.integral.utils.facade.AuthFacade
import com.bumptech.glide.Glide
import com.google.firebase.crashlytics.FirebaseCrashlytics
import dagger.hilt.android.AndroidEntryPoint
import jp.wasabeef.blurry.Blurry
import kotlinx.android.synthetic.main.fragment_profile.*

@AndroidEntryPoint
class ProfileFragment : Fragment(R.layout.fragment_profile) {
private val model: ProfileViewModel by viewModels()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.load()
login?.setOnClickListener {
model.selectLogin()
AuthFacade.goToAuth(
this,
model.configLinks.value?.privacy,
model.configLinks.value?.terms
)
}
model.isLoggedIn.observe(viewLifecycleOwner) {
login?.visibility = if (it) View.GONE else View.VISIBLE
view.post {
blurView?.visibility = View.GONE
scrollView?.visibility = View.VISIBLE
if (!it) {
if (scrollView != null && blurView != null) {
blurView?.visibility = View.VISIBLE
Blurry.with(context)
.radius(10)
.sampling(8)
.capture(scrollView)
.into(blurView)
scrollView?.visibility = View.GONE
}
}
}

}
model.firebaseUser.observe(viewLifecycleOwner) {
name?.text = if (!it?.displayName.isNullOrBlank()) it?.displayName else it?.email
Glide.with(this).load(it?.photoUrl).placeholder(R.mipmap.ic_launcher)
.error(R.mipmap.ic_launcher).into(image)
}
model.xp.observe(viewLifecycleOwner) {
xp?.text = context?.getString(R.string.xp, it.toString())
}
model.level.observe(viewLifecycleOwner) {
level?.text = context?.getString(R.string.level, it.toString())
}
model.progress.observe(viewLifecycleOwner) {
progress?.progress = it
}
model.counterPlot.observe(viewLifecycleOwner) {
statPlot?.statValue = it.toString()
}
model.counterCalculate.observe(viewLifecycleOwner) {
statCalculate?.statValue = it.toString()
}
model.counterFormula.observe(viewLifecycleOwner) {
statFormula?.statValue = it.toString()
}
model.counterAd.observe(viewLifecycleOwner) {
statAdView?.statValue = it.toString()
}
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
AuthFacade.onAuthResult(requestCode, resultCode, data).addOnCompleteListener {
if (it.result != null) model.submitLogin()
}.addOnFailureListener {
FirebaseCrashlytics.getInstance().recordException(it)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package com.armanco.integral.ui.navigation.profile

import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.armanco.integral.data.repository.StatRepository
import com.armanco.integral.utils.XpLevel
import com.armanco.integral.utils.extensions.configLinks
import com.armanco.integral.utils.facade.AuthFacade
import com.armanco.integral.utils.facade.EventFacade
import com.armanco.integral.utils.facade.InitFacade
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class ProfileViewModel @Inject constructor(
private val eventFacade: EventFacade,
private val remoteConfig: FirebaseRemoteConfig,
private val statRepository: StatRepository,
private val initFacade: InitFacade,
): ViewModel() {

val isLoggedIn = MutableLiveData(false)
val firebaseUser = MutableLiveData(AuthFacade.user)
val xp = MutableLiveData(0)
val level = MutableLiveData(1)
val progress = MutableLiveData(0)
val counterPlot = MutableLiveData(0)
val counterCalculate = MutableLiveData(0)
val counterFormula = MutableLiveData(0)
val counterAd = MutableLiveData(0)
var configLinks = MutableLiveData(remoteConfig.configLinks)

fun load() {
isLoggedIn.postValue(AuthFacade.isLoggedIn)
viewModelScope.launch {
update()
}
}

suspend fun update() {
val xpValue = statRepository.xp.first() ?: 0
xp.postValue(xpValue)
val currentLevel = XpLevel.xpToLevel(xpValue)
val minXp = XpLevel.levelToMinXp(currentLevel)
val maxXp = XpLevel.levelToMaxXp(currentLevel)
val currentProgress = (xpValue - minXp) * 100 / (maxXp - minXp)
level.postValue(currentLevel)
progress.postValue(currentProgress)
counterPlot.postValue(statRepository.counterPlot.first() ?: 0)
counterCalculate.postValue(statRepository.counterCalculate.first() ?: 0)
counterFormula.postValue(statRepository.counterFormula.first() ?: 0)
counterAd.postValue(statRepository.counterAd.first() ?: 0)
}

fun selectLogin() {
eventFacade.selectLogin()
}

fun submitLogin() {
eventFacade.selectLogin()
isLoggedIn.postValue(AuthFacade.isLoggedIn)
firebaseUser.postValue(AuthFacade.user)
viewModelScope.launch {
initFacade.init()
update()
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package com.armanco.integral.ui.navigation.settings

import android.content.DialogInterface
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.armanco.integral.R
import com.armanco.integral.data.models.ConfigLinks
import com.armanco.integral.utils.extensions.goToGooglePlay
import com.armanco.integral.utils.extensions.goToUrl
import com.armanco.integral.utils.extensions.isPro
import com.armanco.integral.utils.facade.AuthFacade
import com.armanco.integral.utils.facade.ReviewFacade
import com.google.firebase.crashlytics.FirebaseCrashlytics
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.fragment_settings.*
import me.everything.android.ui.overscroll.OverScrollDecoratorHelper

@AndroidEntryPoint
class SettingsFragment : Fragment(R.layout.fragment_settings) {

private val model: SettingsViewModel by viewModels()

private val reviewFacade = ReviewFacade()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.load()
reviewFacade.init(context)
proVersion?.visibility = if (isPro) View.GONE else View.VISIBLE
scrollView?.let {
OverScrollDecoratorHelper.setUpOverScroll(it)
}
model.isLoggedIn.observe(viewLifecycleOwner) {
delete?.visibility = if (it) View.VISIBLE else View.GONE
logout?.visibility = if (it) View.VISIBLE else View.GONE
login?.visibility = if (it) View.GONE else View.VISIBLE
}
proVersion?.onClick = {
model.selectProVersion()
activity?.goToGooglePlay(
model.configLinks.value?.proPackageName ?: ConfigLinks.PRO_PACKAGE_NAME_DEFAULT
)
}
reportBug?.onClick = {
model.selectReportBug()
activity?.goToUrl(model.configLinks.value?.reportBug ?: ConfigLinks.REPORT_BUG_DEFAULT)
}
contribute?.onClick = {
model.selectContribute()
activity?.goToUrl(model.configLinks.value?.contribute ?: ConfigLinks.CONTRIBUTE_DEFAULT)
}
privacy?.onClick = {
model.selectPrivacy()
activity?.goToUrl(model.configLinks.value?.privacy ?: ConfigLinks.PRIVACY_DEFAULT)
}
terms?.onClick = {
model.selectTerms()
activity?.goToUrl(model.configLinks.value?.terms ?: ConfigLinks.TERMS_DEFAULT)
}
rate?.onClick = {
model.selectRate()
reviewFacade.showReview(activity)
}
delete?.onClick = {
showDeleteDialog()
}
logout?.onClick = {
showLogoutDialog()
}
login?.onClick = {
model.selectLogin()
AuthFacade.goToAuth(
this,
model.configLinks.value?.privacy,
model.configLinks.value?.terms
)
}
reviewFacade.onFinished = {
model.submitRate()
}
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
AuthFacade.onAuthResult(requestCode, resultCode, data).addOnCompleteListener {
if (it.result != null) model.submitLogin()
}.addOnFailureListener {
FirebaseCrashlytics.getInstance().recordException(it)
}
}

private fun showDeleteDialog() {
context?.let { ctx ->
val dialogClickListener =
DialogInterface.OnClickListener { _, type ->
if(type == DialogInterface.BUTTON_POSITIVE) {
model.selectDelete(ctx)
}
}

val builder: AlertDialog.Builder = AlertDialog.Builder(ctx)
builder.setMessage(getString(R.string.dialog_delete_user_text))
.setPositiveButton(getString(R.string.settings_delete), dialogClickListener)
.setNegativeButton(getString(R.string.cancel), dialogClickListener).show()
}
}

private fun showLogoutDialog() {
context?.let { ctx ->
val dialogClickListener =
DialogInterface.OnClickListener { _, type ->
if(type == DialogInterface.BUTTON_POSITIVE) {
model.selectLogout(ctx)
}
}

val builder: AlertDialog.Builder = AlertDialog.Builder(ctx)
builder.setMessage(getString(R.string.are_you_sure))
.setPositiveButton(getString(R.string.settings_logout), dialogClickListener)
.setNegativeButton(getString(R.string.cancel), dialogClickListener).show()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.armanco.integral.ui.navigation.settings

import android.content.Context
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.armanco.integral.data.repository.UserRepository
import com.armanco.integral.utils.extensions.configLinks
import com.armanco.integral.utils.facade.AuthFacade
import com.armanco.integral.utils.facade.EventFacade
import com.armanco.integral.utils.facade.InitFacade
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class SettingsViewModel @Inject constructor(
private val eventFacade: EventFacade,
private val remoteConfig: FirebaseRemoteConfig,
private val initFacade: InitFacade,
private val userRepository: UserRepository
): ViewModel() {

val isLoggedIn = MutableLiveData(false)
var configLinks = MutableLiveData(remoteConfig.configLinks)

fun load() {
isLoggedIn.postValue(AuthFacade.isLoggedIn)
eventFacade.screenView(SettingsFragment::class.java.simpleName)
}

fun selectProVersion() {
eventFacade.selectProVersion()
}

fun selectReportBug() {
eventFacade.selectReportBug()
}

fun selectContribute() {
eventFacade.selectContribute()
}

fun selectPrivacy() {
eventFacade.selectPrivacy()
}

fun selectTerms() {
eventFacade.selectTerms()
}

fun selectRate() {
eventFacade.selectRate()
}

fun submitRate() {
eventFacade.submitRate()
}

fun selectDelete(context: Context) {
AuthFacade.user?.uid?.let {
userRepository.delete(it)
}
AuthFacade.deleteAccount(context)
eventFacade.selectDelete()
isLoggedIn.postValue(false)
}

fun selectLogout(context: Context) {
AuthFacade.signOut(context)
eventFacade.selectLogout()
isLoggedIn.postValue(false)
}

fun selectLogin() {
eventFacade.selectLogin()
}

fun submitLogin() {
isLoggedIn.postValue(AuthFacade.isLoggedIn)
eventFacade.selectLogin()
viewModelScope.launch {
initFacade.init()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.armanco.integral.ui.navigation.solver

import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.armanco.integral.data.models.Solver
import com.armanco.integral.ui.navigation.solver.page.IntegralSolverViewPager
import com.armanco.integral.ui.navigation.solver.page.InverseTrigonometrySolverViewPager
import com.armanco.integral.ui.navigation.solver.page.TrigonometrySolverViewPager

class SolverAdapter(fragment: Fragment, val solvers: List<Solver>) : FragmentStateAdapter(fragment) {

override fun getItemCount(): Int = solvers.size

override fun createFragment(position: Int): Fragment {
return when(solvers.getOrNull(position)) {
Solver.TRIGONOMETRY -> TrigonometrySolverViewPager()
Solver.INVERSE_TRIGONOMETRY -> InverseTrigonometrySolverViewPager()
else -> IntegralSolverViewPager()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.armanco.integral.ui.navigation.solver

import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.armanco.integral.R
import com.armanco.integral.data.models.Solver
import com.armanco.integral.utils.extensions.isTrigonometryOrTrigonometryPersian
import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.fragment_solver.*

@AndroidEntryPoint
class SolverFragment: Fragment(R.layout.fragment_solver) {
private val model: SolverViewModel by viewModels()
private var adapter: SolverAdapter? = null

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val solvers = if(isTrigonometryOrTrigonometryPersian) {
listOf( Solver.TRIGONOMETRY, Solver.INVERSE_TRIGONOMETRY, Solver.INTEGRAL )
} else {
listOf( Solver.INTEGRAL, Solver.TRIGONOMETRY, Solver.INVERSE_TRIGONOMETRY )
}
adapter = SolverAdapter(this, solvers)
viewPager?.adapter = adapter
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
tab.text = when(solvers.getOrNull(position)) {
Solver.TRIGONOMETRY -> context?.getString(R.string.trigonometry)
Solver.INVERSE_TRIGONOMETRY -> context?.getString(R.string.inverse_trigonometry)
else -> context?.getString(R.string.integral)
}
}.attach()
model.load()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.armanco.integral.ui.navigation.solver

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.armanco.integral.data.repository.SolverRepository
import com.armanco.integral.data.repository.StatRepository
import com.armanco.integral.utils.extensions.configAds
import com.armanco.integral.utils.facade.EventFacade
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class SolverViewModel @Inject constructor(
private val repository: SolverRepository,
private val statRepository: StatRepository,
private val eventFacade: EventFacade,
private val remoteConfig: FirebaseRemoteConfig,
): ViewModel() {

fun load() {
eventFacade.screenView(SolverFragment::class.java.simpleName)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package com.armanco.integral.ui.navigation.solver.page

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.armanco.integral.data.repository.SolverRepository
import com.armanco.integral.data.repository.StatRepository
import com.armanco.integral.utils.extensions.configAds
import com.armanco.integral.utils.facade.EventFacade
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import java.lang.Exception
import javax.inject.Inject

@HiltViewModel
class IntegralSolverViewModel @Inject constructor(
private val repository: SolverRepository,
private val statRepository: StatRepository,
private val eventFacade: EventFacade,
private val remoteConfig: FirebaseRemoteConfig,
) : ViewModel() {
val configAds = MutableLiveData(remoteConfig.configAds)
val result = MutableLiveData<Double?>()
val method = MutableLiveData(Method.Romberg)
val hasSteps = MutableLiveData(true)
val function = MutableLiveData("sin(x)")
val steps = MutableLiveData(1)
val lowerLimit = MutableLiveData(0.0)
val upperLimit = MutableLiveData(1.0)
val lastFunction = MutableLiveData(function.value)
val error = MutableLiveData<String?>()

fun load() {
eventFacade.screenView(IntegralSolverViewPager::class.java.simpleName)
}

fun setMethod(method: Method) {
this.method.value = method
this.hasSteps.value = (method == Method.Romberg)
}

fun calculate() {
try {
when (method.value) {
Method.Trapezoidal -> result.postValue(
repository.trapezoidal(
function.value ?: "",
lowerLimit.value ?: 0.0,
upperLimit.value ?: 0.0
)
)
Method.Simpson -> result.postValue(
repository.simpson(
function.value ?: "",
lowerLimit.value ?: 0.0,
upperLimit.value ?: 0.0
)
)
Method.Romberg -> result.postValue(
repository.romberg(
function.value ?: "",
lowerLimit.value ?: 0.0,
upperLimit.value ?: 0.0,
steps.value ?: 1
)
)
}
error.postValue(null)
} catch (e: Exception) {
error.postValue(e.message)
}
viewModelScope.launch {
if (lastFunction.value != function.value) {
lastFunction.postValue(function.value)
statRepository.increaseXp(StatRepository.XP_CALCULATE)
val counter = statRepository.increaseCounterCalculate()
eventFacade.calculate(
method = method.value.toString(),
function = function.value,
lowerLimit = lowerLimit.value,
upperLimit = upperLimit.value,
steps = steps.value,
result = result.value,
counter = counter
)
} else {
eventFacade.calculate(
method = method.value.toString(),
function = function.value,
lowerLimit = lowerLimit.value,
upperLimit = upperLimit.value,
steps = steps.value,
result = result.value
)
}
}
}

fun adShown() {
viewModelScope.launch {
statRepository.increaseXp(StatRepository.XP_AD)
statRepository.increaseCounterAd()
}
}

companion object {
enum class Method {
Trapezoidal,
Simpson,
Romberg
}
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
package com.armanco.integral.navigation.solver
package com.armanco.integral.ui.navigation.solver.page

import android.os.Bundle
import android.view.View
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.armanco.integral.R
import com.armanco.integral.utils.extensions.showRewardedRandom
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.fragment_solver.*
import kotlinx.android.synthetic.main.view_pager_integral_solver.*
import me.everything.android.ui.overscroll.OverScrollDecoratorHelper

@AndroidEntryPoint
class SolverFragment: Fragment(R.layout.fragment_solver) {
private val model: SolverViewModel by viewModels()
class IntegralSolverViewPager: Fragment(R.layout.view_pager_integral_solver) {
private val model: IntegralSolverViewModel by viewModels()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.load()
initValues()
calculate?.setOnClickListener {
activity?.showRewardedRandom(model.configAds.value) {
model.adShown()
}
model.calculate()
}
scrollView?.let {
@@ -42,6 +46,9 @@ class SolverFragment: Fragment(R.layout.fragment_solver) {
model.result.observe(viewLifecycleOwner) { res ->
result?.text = res?.toString()
}
model.error.observe(viewLifecycleOwner) {
function?.error = if(!it.isNullOrBlank()) it else null
}
}

private fun initValues() {
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.armanco.integral.ui.navigation.solver.page

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.armanco.integral.data.models.AngleUnit
import com.armanco.integral.data.repository.SolverRepository
import com.armanco.integral.data.repository.StatRepository
import com.armanco.integral.utils.extensions.configAds
import com.armanco.integral.utils.extensions.round
import com.armanco.integral.utils.facade.EventFacade
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlin.math.*
import kotlin.math.cos
import kotlin.math.sin

@HiltViewModel
class InverseTrigonometrySolverViewModel @Inject constructor(
private val repository: SolverRepository,
private val statRepository: StatRepository,
private val eventFacade: EventFacade,
private val remoteConfig: FirebaseRemoteConfig,
): ViewModel() {
val configAds = MutableLiveData(remoteConfig.configAds)
val arc = MutableLiveData(0.0)
val asin = MutableLiveData(0.0)
val acos = MutableLiveData(0.0)
val atan = MutableLiveData(0.0)
val acot = MutableLiveData(0.0)
val acsc = MutableLiveData(0.0)
val asec = MutableLiveData(0.0)
val unit = MutableLiveData(AngleUnit.DEGREE)

fun load() {
eventFacade.screenView(TrigonometrySolverViewPager::class.java.simpleName)
}

private fun calculate(arc: Double?, unit: AngleUnit?) {
val multiplier = when(unit) {
AngleUnit.RADIAN -> 1.0
else -> 180.0 / PI
}
asin.postValue((asin(arc ?: 0.0)*multiplier).round(4))
acos.postValue((acos(arc ?: 0.0)*multiplier).round(4))
atan.postValue((atan(arc ?: 0.0)*multiplier).round(4))
acot.postValue(((PI / 2 - atan(arc ?: 0.0))*multiplier).round(4))
acsc.postValue((asin(1 / (arc ?: 0.0))*multiplier).round(4))
asec.postValue((acos(1 / (arc ?: 0.0))*multiplier).round(4))
}

fun adShown() {
viewModelScope.launch {
statRepository.increaseXp(StatRepository.XP_AD)
statRepository.increaseCounterAd()
}
}

fun changeUnit(unit: AngleUnit) {
this.unit.postValue(unit)
calculate(arc.value, unit)
}

fun changeArc(arc: Double?) {
this.arc.postValue(arc)
calculate(arc, unit.value)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.armanco.integral.ui.navigation.solver.page

import android.os.Bundle
import android.view.View
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.armanco.integral.R
import com.armanco.integral.data.models.AngleUnit
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.view_pager_inverse_trigonometry_solver.*

@AndroidEntryPoint
class InverseTrigonometrySolverViewPager: Fragment(R.layout.view_pager_inverse_trigonometry_solver) {
private val model: InverseTrigonometrySolverViewModel by viewModels()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.load()
model.asin.observe(viewLifecycleOwner) {
asinScreen?.result = it.toString()
}
model.acos.observe(viewLifecycleOwner) {
acosScreen?.result = it.toString()
}
model.atan.observe(viewLifecycleOwner) {
atanScreen?.result = it.toString()
}
model.acot.observe(viewLifecycleOwner) {
acotScreen?.result = it.toString()
}
model.acsc.observe(viewLifecycleOwner) {
acscScreen?.result = it.toString()
}
model.asec.observe(viewLifecycleOwner) {
asecScreen?.result = it.toString()
}
toggleGroup.isSelectionRequired = true
arc?.addTextChangedListener {
model.changeArc(it.toString().toDoubleOrNull())
}
degree?.setOnClickListener {
model.changeUnit(AngleUnit.DEGREE)
}
radian?.setOnClickListener {
model.changeUnit(AngleUnit.RADIAN)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.armanco.integral.ui.navigation.solver.page

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.armanco.integral.data.models.AngleUnit
import com.armanco.integral.data.repository.SolverRepository
import com.armanco.integral.data.repository.StatRepository
import com.armanco.integral.utils.extensions.configAds
import com.armanco.integral.utils.extensions.round
import com.armanco.integral.utils.facade.EventFacade
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.tan

@HiltViewModel
class TrigonometrySolverViewModel @Inject constructor(
private val repository: SolverRepository,
private val statRepository: StatRepository,
private val eventFacade: EventFacade,
private val remoteConfig: FirebaseRemoteConfig,
): ViewModel() {
val configAds = MutableLiveData(remoteConfig.configAds)
val angle = MutableLiveData(0.0)
val sin = MutableLiveData(0.0)
val cos = MutableLiveData(0.0)
val tan = MutableLiveData(0.0)
val cot = MutableLiveData(0.0)
val csc = MutableLiveData(0.0)
val sec = MutableLiveData(0.0)
val unit = MutableLiveData(AngleUnit.DEGREE)

fun load() {
eventFacade.screenView(TrigonometrySolverViewPager::class.java.simpleName)
}

private fun calculate(angle: Double?, unit: AngleUnit?) {
val radianAngle = when(unit) {
AngleUnit.RADIAN -> angle ?: 0.0
else -> (angle ?: 0.0) * PI / 180.0
}
val sinVal = sin(radianAngle)
val cosVal = cos(radianAngle)
val tanVal = tan(radianAngle)
sin.postValue(sinVal.round(4))
cos.postValue(cosVal.round(4))
tan.postValue(tanVal.round(4))
cot.postValue((1 / tanVal).round(4))
csc.postValue((1 / sinVal).round(4))
sec.postValue((1 / cosVal).round(4))
}

fun adShown() {
viewModelScope.launch {
statRepository.increaseXp(StatRepository.XP_AD)
statRepository.increaseCounterAd()
}
}

fun changeUnit(unit: AngleUnit) {
this.unit.postValue(unit)
calculate(angle.value, unit)
}

fun changeAngle(angle: Double?) {
this.angle.postValue(angle)
calculate(angle, unit.value)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.armanco.integral.ui.navigation.solver.page

import android.os.Bundle
import android.view.View
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.armanco.integral.R
import com.armanco.integral.data.models.AngleUnit
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.view_pager_trigonometry_solver.*

@AndroidEntryPoint
class TrigonometrySolverViewPager: Fragment(R.layout.view_pager_trigonometry_solver) {
private val model: TrigonometrySolverViewModel by viewModels()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.load()
model.sin.observe(viewLifecycleOwner) {
sinScreen?.result = it.toString()
}
model.cos.observe(viewLifecycleOwner) {
cosScreen?.result = it.toString()
}
model.tan.observe(viewLifecycleOwner) {
tanScreen?.result = it.toString()
}
model.cot.observe(viewLifecycleOwner) {
cotScreen?.result = it.toString()
}
model.csc.observe(viewLifecycleOwner) {
cscScreen?.result = it.toString()
}
model.sec.observe(viewLifecycleOwner) {
secScreen?.result = it.toString()
}
toggleGroup.isSelectionRequired = true
angle?.addTextChangedListener {
model.changeAngle(it.toString().toDoubleOrNull())
}
degree?.setOnClickListener {
model.changeUnit(AngleUnit.DEGREE)
}
radian?.setOnClickListener {
model.changeUnit(AngleUnit.RADIAN)
}
}
}
18 changes: 18 additions & 0 deletions app/src/main/java/com/armanco/integral/utils/XpLevel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.armanco.integral.utils

import kotlin.math.floor
import kotlin.math.sqrt

object XpLevel {
fun xpToLevel(xp: Int): Int {
return floor((1.0 + sqrt(1.0 + 4.0 * xp.toDouble() / 25.0)) / 2.0).toInt()
}

fun levelToMinXp(level: Int): Int {
return level * (level - 1) * 25
}

fun levelToMaxXp(level: Int): Int {
return levelToMinXp(level+1)
}
}
Original file line number Diff line number Diff line change
@@ -4,6 +4,19 @@ import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import com.armanco.integral.AppConstants
import com.armanco.integral.BuildConfig
import com.armanco.integral.data.models.ConfigAds
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.LoadAdError
import com.google.android.gms.ads.OnUserEarnedRewardListener
import com.google.android.gms.ads.interstitial.InterstitialAd
import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback
import com.google.android.gms.ads.rewarded.RewardedAd
import com.google.android.gms.ads.rewarded.RewardedAdLoadCallback
import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAd
import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAdLoadCallback
import kotlin.random.Random


fun Activity.goToGooglePlay(appPackageName: String) {
@@ -24,3 +37,64 @@ fun Activity.goToUrl(url: String?) {
val intent = Intent(Intent.ACTION_VIEW, uri)
startActivity(intent)
}


fun Activity.showInterstitialAd(interstitialId: String?) {
if(interstitialId.isNullOrBlank()) return
val adRequest = AdRequest.Builder().build()
InterstitialAd.load(
this,
interstitialId,
adRequest,
object : InterstitialAdLoadCallback() {
override fun onAdLoaded(interstitialAd: InterstitialAd) {
interstitialAd.show(this@showInterstitialAd)
}
})
}


fun Activity.showRewardedInterstitial(rewardedInterstitialId: String?, onUserEarnedRewardListener: OnUserEarnedRewardListener) {
if(rewardedInterstitialId.isNullOrBlank()) return
RewardedInterstitialAd.load(this, rewardedInterstitialId,
AdRequest.Builder().build(), object : RewardedInterstitialAdLoadCallback() {
override fun onAdLoaded(ad: RewardedInterstitialAd) {
ad.show(this@showRewardedInterstitial, onUserEarnedRewardListener)
}
})
}


fun Activity.showRewarded(rewardedId: String?, onUserEarnedRewardListener: OnUserEarnedRewardListener) {
if(rewardedId.isNullOrBlank()) return
RewardedAd.load(this, rewardedId,
AdRequest.Builder().build(), object : RewardedAdLoadCallback() {
override fun onAdLoaded(ad: RewardedAd) {
ad.show(this@showRewarded, onUserEarnedRewardListener)
}
})
}

fun Activity.showRewardedRandom(configAds: ConfigAds?, adShown: ()->Unit): Boolean {
val show = Random.nextInt(0, configAds?.randomSize ?: 2) == 0
if(show) {
showRewarded(when (BuildConfig.FLAVOR) {
AppConstants.FLAVOR_PERSIAN -> {
configAds?.integralPersian?.rewardedId
}
AppConstants.FLAVOR_FREE -> {
configAds?.integral?.rewardedId
}
AppConstants.FLAVOR_TRIGONOMETRY -> {
configAds?.trigonometry?.rewardedId
}
AppConstants.FLAVOR_TRIGONOMETRY_PERSIAN -> {
configAds?.trigonometryPersian?.rewardedId
}
else -> null
}) {
adShown.invoke()
}
}
return show
}
Original file line number Diff line number Diff line change
@@ -2,6 +2,8 @@ package com.armanco.integral.utils.extensions

import android.content.Context
import android.os.Build
import androidx.datastore.preferences.preferencesDataStore
import com.armanco.integral.AppConstants.DATASTORE
import java.util.*

fun Context.setLocale(locale: Locale) {
@@ -31,4 +33,6 @@ val Context.currentLanguage: String
val Context.currentCountry: String
get() {
return currentLocale.country
}
}

val Context.dataStore by preferencesDataStore(DATASTORE)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.armanco.integral.utils.extensions

fun Double.round(decimals: Int): Double {
var multiplier = 1.0
repeat(decimals) { multiplier *= 10 }
return kotlin.math.round(this * multiplier) / multiplier
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.armanco.integral.utils.extensions

import com.armanco.integral.models.ConfigAds
import com.armanco.integral.models.ConfigLinks
import com.armanco.integral.data.models.ConfigAds
import com.armanco.integral.data.models.ConfigLinks
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import com.google.gson.Gson

Original file line number Diff line number Diff line change
@@ -2,11 +2,10 @@ package com.armanco.integral.utils.extensions

import androidx.core.content.ContextCompat
import com.armanco.integral.R
import com.armanco.integral.models.PlotEntries
import com.armanco.integral.data.models.PlotEntries
import com.github.mikephil.charting.charts.LineChart
import com.github.mikephil.charting.components.Legend
import com.github.mikephil.charting.components.XAxis
import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.data.LineData
import com.github.mikephil.charting.data.LineDataSet

Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
package com.armanco.integral.utils.extensions

import com.armanco.integral.AppConstants
import com.armanco.integral.BuildConfig

val isPro: Boolean
val isTrigonometryOrTrigonometryPersian: Boolean
get() {
return when (BuildConfig.FLAVOR) {
"pro" -> true
else -> false
return BuildConfig.FLAVOR == AppConstants.FLAVOR_TRIGONOMETRY || BuildConfig.FLAVOR == AppConstants.FLAVOR_TRIGONOMETRY_PERSIAN
}
}

val isPersian: Boolean
val isPro: Boolean
get() {
return when (BuildConfig.FLAVOR) {
"persian" -> true
else -> false
return BuildConfig.FLAVOR == AppConstants.FLAVOR_PRO
}
}

val isIntegralPersianOrTrigonometryPersian: Boolean
get() {
return BuildConfig.FLAVOR == AppConstants.FLAVOR_PERSIAN || BuildConfig.FLAVOR == AppConstants.FLAVOR_TRIGONOMETRY_PERSIAN
}
90 changes: 90 additions & 0 deletions app/src/main/java/com/armanco/integral/utils/facade/AuthFacade.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.armanco.integral.utils.facade

import android.app.Activity
import android.content.Context
import android.content.Intent
import androidx.fragment.app.Fragment
import com.armanco.integral.R
import com.firebase.ui.auth.AuthUI
import com.firebase.ui.auth.IdpResponse
import com.google.android.gms.tasks.Task
import com.google.android.gms.tasks.Tasks
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseUser
import java.lang.Exception

object AuthFacade {
fun goToAuth(activity: Activity, privacyUrl: String?, termsUrl: String?) {
val providers = arrayListOf(
AuthUI.IdpConfig.EmailBuilder().build(),
AuthUI.IdpConfig.PhoneBuilder().build(),
AuthUI.IdpConfig.GoogleBuilder().build())

activity.startActivityForResult(
AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(providers)
.setLogo(R.mipmap.ic_launcher)
.setTheme(R.style.Theme_Integral)
.apply {
if(!termsUrl.isNullOrBlank() && !privacyUrl.isNullOrBlank()) {
this.setTosAndPrivacyPolicyUrls(termsUrl, privacyUrl)
}
}
.build(),
AUTH_UI_CODE)
}

fun goToAuth(fragment: Fragment, privacyUrl: String?, termsUrl: String?) {
val providers = arrayListOf(
AuthUI.IdpConfig.EmailBuilder().build(),
AuthUI.IdpConfig.PhoneBuilder().build(),
AuthUI.IdpConfig.GoogleBuilder().build())

fragment.startActivityForResult(
AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(providers)
.setLogo(R.mipmap.ic_launcher)
.setTheme(R.style.Theme_Integral)
.apply {
if(!termsUrl.isNullOrBlank() && !privacyUrl.isNullOrBlank()) {
this.setTosAndPrivacyPolicyUrls(termsUrl, privacyUrl)
}
}
.build(),
AUTH_UI_CODE)
}

fun signOut(context: Context): Task<Void> {
return AuthUI.getInstance()
.signOut(context)
}

fun deleteAccount(context: Context): Task<Void> {
return AuthUI.getInstance()
.delete(context)
}

fun onAuthResult(requestCode: Int, resultCode: Int, data: Intent?): Task<FirebaseUser> {
if (requestCode == AUTH_UI_CODE) {
if (resultCode == Activity.RESULT_OK) {
return Tasks.forResult(FirebaseAuth.getInstance().currentUser)
}
}
val response = IdpResponse.fromResultIntent(data)
return Tasks.forException(response?.error ?: Exception())
}

val isLoggedIn: Boolean
get() {
return !FirebaseAuth.getInstance().currentUser?.uid.isNullOrBlank()
}

val user: FirebaseUser?
get() {
return FirebaseAuth.getInstance().currentUser
}

private const val AUTH_UI_CODE = 2000
}
34 changes: 31 additions & 3 deletions app/src/main/java/com/armanco/integral/utils/facade/EventFacade.kt
Original file line number Diff line number Diff line change
@@ -33,35 +33,38 @@ class EventFacade {
))
}

fun selectImage(id: Int, categoryId: Int, title: String?) {
fun selectImage(id: Int, categoryId: Int, title: String?, counter: Int?) {
firebaseAnalytics?.logEvent(Event.SELECT_IMAGE, bundleOf(
Param.ID to id.toString(),
Param.CATEGORY to categoryId.toString(),
Param.TITLE to title,
Param.COUNTER to counter,
Param.LANGUAGE to context?.currentLanguage,
Param.COUNTRY to context?.currentCountry,
))
}

fun calculate(method: String?, function: String?, lowerLimit: Double?, upperLimit: Double?, steps: Int?, result: Double?) {
fun calculate(method: String?, function: String?, lowerLimit: Double?, upperLimit: Double?, steps: Int?, result: Double?, counter: Int? = null) {
firebaseAnalytics?.logEvent(Event.CALCULATE, bundleOf(
Param.METHOD to method,
Param.FUNCTION to function,
Param.LOWER_LIMIT to lowerLimit.toString(),
Param.UPPER_LIMIT to upperLimit.toString(),
Param.STEPS to steps.toString(),
Param.RESULT to result.toString(),
Param.COUNTER to counter,
Param.LANGUAGE to context?.currentLanguage,
Param.COUNTRY to context?.currentCountry,
))
}

fun plot(function: String?, lowerLimit: Double?, upperLimit: Double?, steps: Int?) {
fun plot(function: String?, lowerLimit: Double?, upperLimit: Double?, steps: Int?, counter: Int? = null) {
firebaseAnalytics?.logEvent(Event.PLOT, bundleOf(
Param.FUNCTION to function,
Param.LOWER_LIMIT to lowerLimit.toString(),
Param.UPPER_LIMIT to upperLimit.toString(),
Param.STEPS to steps.toString(),
Param.COUNTER to counter,
Param.LANGUAGE to context?.currentLanguage,
Param.COUNTRY to context?.currentCountry,
))
@@ -116,6 +119,27 @@ class EventFacade {
))
}

fun selectDelete() {
firebaseAnalytics?.logEvent(Event.SELECT_DELETE, bundleOf(
Param.LANGUAGE to context?.currentLanguage,
Param.COUNTRY to context?.currentCountry,
))
}

fun selectLogin() {
firebaseAnalytics?.logEvent(Event.SELECT_LOGIN, bundleOf(
Param.LANGUAGE to context?.currentLanguage,
Param.COUNTRY to context?.currentCountry,
))
}

fun selectLogout() {
firebaseAnalytics?.logEvent(Event.SELECT_LOGOUT, bundleOf(
Param.LANGUAGE to context?.currentLanguage,
Param.COUNTRY to context?.currentCountry,
))
}


companion object {
object Event {
@@ -130,6 +154,9 @@ class EventFacade {
const val SELECT_TERMS = "select_terms"
const val SELECT_RATE = "select_rate"
const val SUBMIT_RATE = "submit_rate"
const val SELECT_DELETE = "select_delete"
const val SELECT_LOGIN = "select_login"
const val SELECT_LOGOUT = "select_logout"
}
object Param {
const val LANGUAGE = "language"
@@ -143,6 +170,7 @@ class EventFacade {
const val UPPER_LIMIT = "upper_limit"
const val RESULT = "result"
const val STEPS = "steps"
const val COUNTER = "counter"
}
}
}
83 changes: 83 additions & 0 deletions app/src/main/java/com/armanco/integral/utils/facade/InitFacade.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.armanco.integral.utils.facade

import com.armanco.integral.data.models.User
import com.armanco.integral.data.repository.StatRepository
import com.armanco.integral.data.repository.UserRepository
import com.google.firebase.auth.FirebaseUser
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.firebase.firestore.ktx.toObject
import com.google.firebase.messaging.FirebaseMessaging
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.tasks.await
import javax.inject.Inject
import kotlin.math.max

class InitFacade @Inject constructor(
private val statRepository: StatRepository,
private val userRepository: UserRepository,
) {

suspend fun init() {
AuthFacade.user?.let {
updateUserLocal(it)?.let { it1 -> updateUserFirestore(it, it1) }
}
}

private suspend fun getMessagingToken(): String? {
return try {
FirebaseMessaging.getInstance().token.await()
} catch (exception: java.lang.Exception) {
FirebaseCrashlytics.getInstance().recordException(exception)
null
}
}

private suspend fun updateUserLocal(firebaseUser: FirebaseUser): User? {
try {
val documentSnapshot = userRepository.get(firebaseUser.uid).await()
val user: User =
documentSnapshot.toObject<User>() ?: User.fromFirebaseUser(firebaseUser)
user.let {
val xp = max(statRepository.xp.first() ?: 0, it.xp ?: 0)
val counterPlot = max(statRepository.counterPlot.first() ?: 0, it.counterPlot ?: 0)
val counterCalculate =
max(statRepository.counterCalculate.first() ?: 0, it.counterCalculate ?: 0)
val counterFormula =
max(statRepository.counterFormula.first() ?: 0, it.counterFormula ?: 0)
val counterAd = max(statRepository.counterAd.first() ?: 0, it.counterAd ?: 0)

statRepository.setXp(xp)
statRepository.setCounterPlot(counterPlot)
statRepository.setCounterCalculate(counterCalculate)
statRepository.setCounterFormula(counterFormula)
statRepository.setCounterAd(counterAd)

it.xp = xp
it.counterPlot = counterPlot
it.counterCalculate = counterCalculate
it.counterFormula = counterFormula
it.counterAd = counterAd
}
return user
} catch (exception: Exception) {
FirebaseCrashlytics.getInstance().recordException(exception)
return null
}
}

private suspend fun updateUserFirestore(firebaseUser: FirebaseUser, user: User) {
try {
userRepository.set(User.fromFirebaseUser(firebaseUser).apply {
xp = user.xp
counterPlot = user.counterPlot
counterCalculate = user.counterCalculate
counterFormula = user.counterFormula
counterAd = user.counterAd
messagingToken = getMessagingToken()
})?.await()
} catch (exception: Exception) {
FirebaseCrashlytics.getInstance().recordException(exception)
}
}

}
Binary file added app/src/main/res/drawable-hdpi/tri_10_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_10_10.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_10_11.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_10_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_10_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_10_4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_10_5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_10_6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_10_7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_10_8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_10_9.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_11_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_11_10.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_11_11.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_11_12.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_11_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_11_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_11_4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_11_5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_11_6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_11_7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_11_8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_11_9.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_12_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_12_10.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-hdpi/tri_12_11.png
Binary file added app/src/main/res/drawable-hdpi/tri_12_12.png
Binary file added app/src/main/res/drawable-hdpi/tri_12_13.png
Binary file added app/src/main/res/drawable-hdpi/tri_12_2.png
Binary file added app/src/main/res/drawable-hdpi/tri_12_3.png
Binary file added app/src/main/res/drawable-hdpi/tri_12_4.png
Binary file added app/src/main/res/drawable-hdpi/tri_12_5.png
Binary file added app/src/main/res/drawable-hdpi/tri_12_6.png
Binary file added app/src/main/res/drawable-hdpi/tri_12_7.png
Binary file added app/src/main/res/drawable-hdpi/tri_12_8.png
Binary file added app/src/main/res/drawable-hdpi/tri_12_9.png
Binary file added app/src/main/res/drawable-hdpi/tri_1_1.png
Binary file added app/src/main/res/drawable-hdpi/tri_1_2.png
Binary file added app/src/main/res/drawable-hdpi/tri_1_3.png
Binary file added app/src/main/res/drawable-hdpi/tri_1_4.png
Binary file added app/src/main/res/drawable-hdpi/tri_1_5.png
Binary file added app/src/main/res/drawable-hdpi/tri_1_6.png
Binary file added app/src/main/res/drawable-hdpi/tri_1_7.png
Binary file added app/src/main/res/drawable-hdpi/tri_2_1.png
Binary file added app/src/main/res/drawable-hdpi/tri_2_2.png
Binary file added app/src/main/res/drawable-hdpi/tri_2_3.png
Binary file added app/src/main/res/drawable-hdpi/tri_2_4.png
Binary file added app/src/main/res/drawable-hdpi/tri_2_5.png
Binary file added app/src/main/res/drawable-hdpi/tri_2_6.png
Binary file added app/src/main/res/drawable-hdpi/tri_3_1.png
Binary file added app/src/main/res/drawable-hdpi/tri_3_2.png
Binary file added app/src/main/res/drawable-hdpi/tri_3_3.png
Binary file added app/src/main/res/drawable-hdpi/tri_3_4.png
Binary file added app/src/main/res/drawable-hdpi/tri_3_5.png
Binary file added app/src/main/res/drawable-hdpi/tri_3_6.png
Binary file added app/src/main/res/drawable-hdpi/tri_4_1.png
Binary file added app/src/main/res/drawable-hdpi/tri_4_2.png
Binary file added app/src/main/res/drawable-hdpi/tri_4_3.png
Binary file added app/src/main/res/drawable-hdpi/tri_4_4.png
Binary file added app/src/main/res/drawable-hdpi/tri_5_1.png
Binary file added app/src/main/res/drawable-hdpi/tri_5_2.png
Binary file added app/src/main/res/drawable-hdpi/tri_5_3.png
Binary file added app/src/main/res/drawable-hdpi/tri_5_4.png
Binary file added app/src/main/res/drawable-hdpi/tri_6_1.png
Binary file added app/src/main/res/drawable-hdpi/tri_6_2.png
Binary file added app/src/main/res/drawable-hdpi/tri_6_3.png
Binary file added app/src/main/res/drawable-hdpi/tri_6_4.png
Binary file added app/src/main/res/drawable-hdpi/tri_7_1.png
Binary file added app/src/main/res/drawable-hdpi/tri_7_2.png
Binary file added app/src/main/res/drawable-hdpi/tri_7_3.png
Binary file added app/src/main/res/drawable-hdpi/tri_7_4.png
Binary file added app/src/main/res/drawable-hdpi/tri_7_5.png
Binary file added app/src/main/res/drawable-hdpi/tri_7_6.png
Binary file added app/src/main/res/drawable-hdpi/tri_7_7.png
Binary file added app/src/main/res/drawable-hdpi/tri_7_8.png
Binary file added app/src/main/res/drawable-hdpi/tri_8_1.png
Binary file added app/src/main/res/drawable-hdpi/tri_8_2.png
Binary file added app/src/main/res/drawable-hdpi/tri_8_3.png
Binary file added app/src/main/res/drawable-hdpi/tri_8_4.png
Binary file added app/src/main/res/drawable-hdpi/tri_8_5.png
Binary file added app/src/main/res/drawable-hdpi/tri_9_1.png
Binary file added app/src/main/res/drawable-hdpi/tri_9_2.png
Binary file added app/src/main/res/drawable-hdpi/tri_9_3.png
Binary file added app/src/main/res/drawable-ldpi/tri_10_1.png
Binary file added app/src/main/res/drawable-ldpi/tri_10_10.png
Binary file added app/src/main/res/drawable-ldpi/tri_10_11.png
Binary file added app/src/main/res/drawable-ldpi/tri_10_2.png
Binary file added app/src/main/res/drawable-ldpi/tri_10_3.png
Binary file added app/src/main/res/drawable-ldpi/tri_10_4.png
Binary file added app/src/main/res/drawable-ldpi/tri_10_5.png
Binary file added app/src/main/res/drawable-ldpi/tri_10_6.png
Binary file added app/src/main/res/drawable-ldpi/tri_10_7.png
Binary file added app/src/main/res/drawable-ldpi/tri_10_8.png
Binary file added app/src/main/res/drawable-ldpi/tri_10_9.png
Binary file added app/src/main/res/drawable-ldpi/tri_11_1.png
Binary file added app/src/main/res/drawable-ldpi/tri_11_10.png
Binary file added app/src/main/res/drawable-ldpi/tri_11_11.png
Binary file added app/src/main/res/drawable-ldpi/tri_11_12.png
Binary file added app/src/main/res/drawable-ldpi/tri_11_2.png
Binary file added app/src/main/res/drawable-ldpi/tri_11_3.png
Binary file added app/src/main/res/drawable-ldpi/tri_11_4.png
Binary file added app/src/main/res/drawable-ldpi/tri_11_5.png
Binary file added app/src/main/res/drawable-ldpi/tri_11_6.png
Binary file added app/src/main/res/drawable-ldpi/tri_11_7.png
Binary file added app/src/main/res/drawable-ldpi/tri_11_8.png
Binary file added app/src/main/res/drawable-ldpi/tri_11_9.png
Binary file added app/src/main/res/drawable-ldpi/tri_12_1.png
Binary file added app/src/main/res/drawable-ldpi/tri_12_10.png
Binary file added app/src/main/res/drawable-ldpi/tri_12_11.png
Binary file added app/src/main/res/drawable-ldpi/tri_12_12.png
Binary file added app/src/main/res/drawable-ldpi/tri_12_13.png
Binary file added app/src/main/res/drawable-ldpi/tri_12_2.png
Binary file added app/src/main/res/drawable-ldpi/tri_12_3.png
Binary file added app/src/main/res/drawable-ldpi/tri_12_4.png
Binary file added app/src/main/res/drawable-ldpi/tri_12_5.png
Binary file added app/src/main/res/drawable-ldpi/tri_12_6.png
Binary file added app/src/main/res/drawable-ldpi/tri_12_7.png
Binary file added app/src/main/res/drawable-ldpi/tri_12_8.png
Binary file added app/src/main/res/drawable-ldpi/tri_12_9.png
Binary file added app/src/main/res/drawable-ldpi/tri_1_1.png
Binary file added app/src/main/res/drawable-ldpi/tri_1_2.png
Binary file added app/src/main/res/drawable-ldpi/tri_1_3.png
Binary file added app/src/main/res/drawable-ldpi/tri_1_4.png
Binary file added app/src/main/res/drawable-ldpi/tri_1_5.png
Binary file added app/src/main/res/drawable-ldpi/tri_1_6.png
Binary file added app/src/main/res/drawable-ldpi/tri_1_7.png
Binary file added app/src/main/res/drawable-ldpi/tri_2_1.png
Binary file added app/src/main/res/drawable-ldpi/tri_2_2.png
Binary file added app/src/main/res/drawable-ldpi/tri_2_3.png
Binary file added app/src/main/res/drawable-ldpi/tri_2_4.png
Binary file added app/src/main/res/drawable-ldpi/tri_2_5.png
Binary file added app/src/main/res/drawable-ldpi/tri_2_6.png
Binary file added app/src/main/res/drawable-ldpi/tri_3_1.png
Binary file added app/src/main/res/drawable-ldpi/tri_3_2.png
Binary file added app/src/main/res/drawable-ldpi/tri_3_3.png
Binary file added app/src/main/res/drawable-ldpi/tri_3_4.png
Binary file added app/src/main/res/drawable-ldpi/tri_3_5.png
Binary file added app/src/main/res/drawable-ldpi/tri_3_6.png
Binary file added app/src/main/res/drawable-ldpi/tri_4_1.png
Binary file added app/src/main/res/drawable-ldpi/tri_4_2.png
Binary file added app/src/main/res/drawable-ldpi/tri_4_3.png
Binary file added app/src/main/res/drawable-ldpi/tri_4_4.png
Binary file added app/src/main/res/drawable-ldpi/tri_5_1.png
Binary file added app/src/main/res/drawable-ldpi/tri_5_2.png
Binary file added app/src/main/res/drawable-ldpi/tri_5_3.png
Binary file added app/src/main/res/drawable-ldpi/tri_5_4.png
Binary file added app/src/main/res/drawable-ldpi/tri_6_1.png
Binary file added app/src/main/res/drawable-ldpi/tri_6_2.png
Binary file added app/src/main/res/drawable-ldpi/tri_6_3.png
Binary file added app/src/main/res/drawable-ldpi/tri_6_4.png
Binary file added app/src/main/res/drawable-ldpi/tri_7_1.png
Binary file added app/src/main/res/drawable-ldpi/tri_7_2.png
Binary file added app/src/main/res/drawable-ldpi/tri_7_3.png
Binary file added app/src/main/res/drawable-ldpi/tri_7_4.png
Binary file added app/src/main/res/drawable-ldpi/tri_7_5.png
Binary file added app/src/main/res/drawable-ldpi/tri_7_6.png
Binary file added app/src/main/res/drawable-ldpi/tri_7_7.png
Binary file added app/src/main/res/drawable-ldpi/tri_7_8.png
Binary file added app/src/main/res/drawable-ldpi/tri_8_1.png
Binary file added app/src/main/res/drawable-ldpi/tri_8_2.png
Binary file added app/src/main/res/drawable-ldpi/tri_8_3.png
Binary file added app/src/main/res/drawable-ldpi/tri_8_4.png
Binary file added app/src/main/res/drawable-ldpi/tri_8_5.png
Binary file added app/src/main/res/drawable-ldpi/tri_9_1.png
Binary file added app/src/main/res/drawable-ldpi/tri_9_2.png
Binary file added app/src/main/res/drawable-ldpi/tri_9_3.png
Binary file added app/src/main/res/drawable-mdpi/tri_10_1.png
Binary file added app/src/main/res/drawable-mdpi/tri_10_10.png
Binary file added app/src/main/res/drawable-mdpi/tri_10_11.png
Binary file added app/src/main/res/drawable-mdpi/tri_10_2.png
Binary file added app/src/main/res/drawable-mdpi/tri_10_3.png
Binary file added app/src/main/res/drawable-mdpi/tri_10_4.png
Binary file added app/src/main/res/drawable-mdpi/tri_10_5.png
Binary file added app/src/main/res/drawable-mdpi/tri_10_6.png
Binary file added app/src/main/res/drawable-mdpi/tri_10_7.png
Binary file added app/src/main/res/drawable-mdpi/tri_10_8.png
Binary file added app/src/main/res/drawable-mdpi/tri_10_9.png
Binary file added app/src/main/res/drawable-mdpi/tri_11_1.png
Binary file added app/src/main/res/drawable-mdpi/tri_11_10.png
Binary file added app/src/main/res/drawable-mdpi/tri_11_11.png
Binary file added app/src/main/res/drawable-mdpi/tri_11_12.png
Binary file added app/src/main/res/drawable-mdpi/tri_11_2.png
Binary file added app/src/main/res/drawable-mdpi/tri_11_3.png
Binary file added app/src/main/res/drawable-mdpi/tri_11_4.png
Binary file added app/src/main/res/drawable-mdpi/tri_11_5.png
Binary file added app/src/main/res/drawable-mdpi/tri_11_6.png
Binary file added app/src/main/res/drawable-mdpi/tri_11_7.png
Binary file added app/src/main/res/drawable-mdpi/tri_11_8.png
Binary file added app/src/main/res/drawable-mdpi/tri_11_9.png
Binary file added app/src/main/res/drawable-mdpi/tri_12_1.png
Binary file added app/src/main/res/drawable-mdpi/tri_12_10.png
Binary file added app/src/main/res/drawable-mdpi/tri_12_11.png
Binary file added app/src/main/res/drawable-mdpi/tri_12_12.png
Binary file added app/src/main/res/drawable-mdpi/tri_12_13.png
Binary file added app/src/main/res/drawable-mdpi/tri_12_2.png
Binary file added app/src/main/res/drawable-mdpi/tri_12_3.png
Binary file added app/src/main/res/drawable-mdpi/tri_12_4.png
Binary file added app/src/main/res/drawable-mdpi/tri_12_5.png
Binary file added app/src/main/res/drawable-mdpi/tri_12_6.png
Binary file added app/src/main/res/drawable-mdpi/tri_12_7.png
Binary file added app/src/main/res/drawable-mdpi/tri_12_8.png
Binary file added app/src/main/res/drawable-mdpi/tri_12_9.png
Binary file added app/src/main/res/drawable-mdpi/tri_1_1.png
Binary file added app/src/main/res/drawable-mdpi/tri_1_2.png
Binary file added app/src/main/res/drawable-mdpi/tri_1_3.png
Binary file added app/src/main/res/drawable-mdpi/tri_1_4.png
Binary file added app/src/main/res/drawable-mdpi/tri_1_5.png
Binary file added app/src/main/res/drawable-mdpi/tri_1_6.png
Binary file added app/src/main/res/drawable-mdpi/tri_1_7.png
Binary file added app/src/main/res/drawable-mdpi/tri_2_1.png
Binary file added app/src/main/res/drawable-mdpi/tri_2_2.png
Binary file added app/src/main/res/drawable-mdpi/tri_2_3.png
Binary file added app/src/main/res/drawable-mdpi/tri_2_4.png
Binary file added app/src/main/res/drawable-mdpi/tri_2_5.png
Binary file added app/src/main/res/drawable-mdpi/tri_2_6.png
Binary file added app/src/main/res/drawable-mdpi/tri_3_1.png
Binary file added app/src/main/res/drawable-mdpi/tri_3_2.png
Binary file added app/src/main/res/drawable-mdpi/tri_3_3.png
Binary file added app/src/main/res/drawable-mdpi/tri_3_4.png
Binary file added app/src/main/res/drawable-mdpi/tri_3_5.png
Binary file added app/src/main/res/drawable-mdpi/tri_3_6.png
Binary file added app/src/main/res/drawable-mdpi/tri_4_1.png
Binary file added app/src/main/res/drawable-mdpi/tri_4_2.png
Binary file added app/src/main/res/drawable-mdpi/tri_4_3.png
Binary file added app/src/main/res/drawable-mdpi/tri_4_4.png
Binary file added app/src/main/res/drawable-mdpi/tri_5_1.png
Binary file added app/src/main/res/drawable-mdpi/tri_5_2.png
Binary file added app/src/main/res/drawable-mdpi/tri_5_3.png
Binary file added app/src/main/res/drawable-mdpi/tri_5_4.png
Binary file added app/src/main/res/drawable-mdpi/tri_6_1.png
Binary file added app/src/main/res/drawable-mdpi/tri_6_2.png
Binary file added app/src/main/res/drawable-mdpi/tri_6_3.png
Binary file added app/src/main/res/drawable-mdpi/tri_6_4.png
Binary file added app/src/main/res/drawable-mdpi/tri_7_1.png
Binary file added app/src/main/res/drawable-mdpi/tri_7_2.png
Binary file added app/src/main/res/drawable-mdpi/tri_7_3.png
Binary file added app/src/main/res/drawable-mdpi/tri_7_4.png
Binary file added app/src/main/res/drawable-mdpi/tri_7_5.png
Binary file added app/src/main/res/drawable-mdpi/tri_7_6.png
Binary file added app/src/main/res/drawable-mdpi/tri_7_7.png
Binary file added app/src/main/res/drawable-mdpi/tri_7_8.png
Binary file added app/src/main/res/drawable-mdpi/tri_8_1.png
Binary file added app/src/main/res/drawable-mdpi/tri_8_2.png
Binary file added app/src/main/res/drawable-mdpi/tri_8_3.png
Binary file added app/src/main/res/drawable-mdpi/tri_8_4.png
Binary file added app/src/main/res/drawable-mdpi/tri_8_5.png
Binary file added app/src/main/res/drawable-mdpi/tri_9_1.png
Binary file added app/src/main/res/drawable-mdpi/tri_9_2.png
Binary file added app/src/main/res/drawable-mdpi/tri_9_3.png
30 changes: 0 additions & 30 deletions app/src/main/res/drawable-v24/ic_launcher_foreground.xml

This file was deleted.

Binary file added app/src/main/res/drawable-xhdpi/tri_10_1.png
Binary file added app/src/main/res/drawable-xhdpi/tri_10_10.png
Binary file added app/src/main/res/drawable-xhdpi/tri_10_11.png
Binary file added app/src/main/res/drawable-xhdpi/tri_10_2.png
Binary file added app/src/main/res/drawable-xhdpi/tri_10_3.png
Binary file added app/src/main/res/drawable-xhdpi/tri_10_4.png
Binary file added app/src/main/res/drawable-xhdpi/tri_10_5.png
Binary file added app/src/main/res/drawable-xhdpi/tri_10_6.png
Binary file added app/src/main/res/drawable-xhdpi/tri_10_7.png
Binary file added app/src/main/res/drawable-xhdpi/tri_10_8.png
Binary file added app/src/main/res/drawable-xhdpi/tri_10_9.png
Binary file added app/src/main/res/drawable-xhdpi/tri_11_1.png
Binary file added app/src/main/res/drawable-xhdpi/tri_11_10.png
Binary file added app/src/main/res/drawable-xhdpi/tri_11_11.png
Binary file added app/src/main/res/drawable-xhdpi/tri_11_12.png
Binary file added app/src/main/res/drawable-xhdpi/tri_11_2.png
Binary file added app/src/main/res/drawable-xhdpi/tri_11_3.png
Binary file added app/src/main/res/drawable-xhdpi/tri_11_4.png
Binary file added app/src/main/res/drawable-xhdpi/tri_11_5.png
Binary file added app/src/main/res/drawable-xhdpi/tri_11_6.png
Binary file added app/src/main/res/drawable-xhdpi/tri_11_7.png
Binary file added app/src/main/res/drawable-xhdpi/tri_11_8.png
Binary file added app/src/main/res/drawable-xhdpi/tri_11_9.png
Binary file added app/src/main/res/drawable-xhdpi/tri_12_1.png
Binary file added app/src/main/res/drawable-xhdpi/tri_12_10.png
Binary file added app/src/main/res/drawable-xhdpi/tri_12_11.png
Binary file added app/src/main/res/drawable-xhdpi/tri_12_12.png
Binary file added app/src/main/res/drawable-xhdpi/tri_12_13.png
Binary file added app/src/main/res/drawable-xhdpi/tri_12_2.png
Binary file added app/src/main/res/drawable-xhdpi/tri_12_3.png
Binary file added app/src/main/res/drawable-xhdpi/tri_12_4.png
Binary file added app/src/main/res/drawable-xhdpi/tri_12_5.png
Binary file added app/src/main/res/drawable-xhdpi/tri_12_6.png
Binary file added app/src/main/res/drawable-xhdpi/tri_12_7.png
Binary file added app/src/main/res/drawable-xhdpi/tri_12_8.png
Binary file added app/src/main/res/drawable-xhdpi/tri_12_9.png
Binary file added app/src/main/res/drawable-xhdpi/tri_1_1.png
Binary file added app/src/main/res/drawable-xhdpi/tri_1_2.png
Binary file added app/src/main/res/drawable-xhdpi/tri_1_3.png
Loading