Skip to content
This repository has been archived by the owner on Nov 5, 2024. It is now read-only.

Commit

Permalink
Merge pull request #30 from ILIYANGERMANOV/develop
Browse files Browse the repository at this point in the history
Milestone #1 Go Open Source!
  • Loading branch information
ILIYANGERMANOV authored Nov 5, 2021
2 parents 5b69d04 + 5831d2c commit f9432de
Show file tree
Hide file tree
Showing 22 changed files with 495 additions and 94 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ captures/
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
#misc.xml is annoying and useless
.idea/misc.xml
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
Expand Down
13 changes: 0 additions & 13 deletions .idea/misc.xml

This file was deleted.

2 changes: 2 additions & 0 deletions app/src/main/java/com/ivy/wallet/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ object Constants {
const val URL_PRIVACY_POLICY =
"https://github.com/ILIYANGERMANOV/privacy-policies/blob/master/ivy-wallet-privacy-policy.md"

const val URL_IVY_WALLET_REPO = "https://github.com/ILIYANGERMANOV/ivy-wallet"

const val URL_IVY_WALLET_GOOGLE_PLAY =
"https://play.google.com/store/apps/details?id=com.ivy.wallet"

Expand Down
7 changes: 6 additions & 1 deletion app/src/main/java/com/ivy/wallet/base/ComposeExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.platform.UriHandler
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.Density
Expand Down Expand Up @@ -193,4 +194,8 @@ fun Dp.toDensityPx() = densityScope { toPx() }
fun Int.toDensityDp() = densityScope { toDp() }

@Composable
fun Float.toDensityDp() = densityScope { toDp() }
fun Float.toDensityDp() = densityScope { toDp() }

fun openUrl(uriHandler: UriHandler, url: String) {
uriHandler.openUri(url)
}
69 changes: 51 additions & 18 deletions app/src/main/java/com/ivy/wallet/logic/CustomerJourneyLogic.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.ivy.wallet.logic

import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.ivy.wallet.Constants
import com.ivy.wallet.R
import com.ivy.wallet.logic.model.CustomerJourneyCardData
import com.ivy.wallet.model.TransactionType
Expand Down Expand Up @@ -55,7 +56,8 @@ class CustomerJourneyLogic(
buyLifetimeOfferCard(),
makeReportCard(),
rateUsCard_2(),
shareIvyWalletCard_2()
shareIvyWalletCard_2(),
ivyWalletIsOpenSource()
)

fun adjustBalanceCard() = CustomerJourneyCardData(
Expand All @@ -81,7 +83,7 @@ class CustomerJourneyLogic(
},
title = "Create your first planned payment",
description = "Automate the tracking of recurring transactions like your subscriptions, rent, salary, etc." +
" Stay ahead of your finances by knowing how much you have to pay/get in advance.",
" Stay ahead of your finances by knowing how much you have to pay/get in advance.",
cta = "Add planned payment",
ctaIcon = R.drawable.ic_planned_payments,
backgroundColor = Orange,
Expand All @@ -103,7 +105,7 @@ class CustomerJourneyLogic(
},
title = "Did you know?",
description = "Ivy Wallet has a cool widget that lets you add INCOME/EXPENSES/TRANSFER transactions with 1-click from your home screen. " +
"\n\nNote: If the \"Add widget\" button doesn't work, please add it manually from your launcher's widgets menu.",
"\n\nNote: If the \"Add widget\" button doesn't work, please add it manually from your launcher's widgets menu.",
cta = "Add widget",
ctaIcon = R.drawable.ic_custom_atom_s,
backgroundColor = GreenLight,
Expand All @@ -120,8 +122,8 @@ class CustomerJourneyLogic(
},
title = "Set a budget",
description = "Ivy Wallet not only helps you to passively track your expenses" +
" but also proactively create your financial future by setting budgets" +
" and sticking to them.",
" but also proactively create your financial future by setting budgets" +
" and sticking to them.",
cta = "Add budget",
ctaIcon = R.drawable.ic_budget_xs,
backgroundColor = Green2,
Expand Down Expand Up @@ -154,8 +156,8 @@ class CustomerJourneyLogic(
},
title = "Review Ivy Wallet",
description = "Give us your feedback! Help Ivy Wallet become better and grow by writing us a review." +
" Compliments, ideas, and critics are all welcome!" +
" We do our best.\n\nCheers,\nIvy Team",
" Compliments, ideas, and critics are all welcome!" +
" We do our best.\n\nCheers,\nIvy Team",
cta = "Rate us on Google Play",
ctaIcon = R.drawable.ic_custom_star_s,
backgroundColor = Green,
Expand All @@ -172,7 +174,7 @@ class CustomerJourneyLogic(
},
title = "Share Ivy Wallet",
description = "Help us grow so we can invest more in development and make the app better for you." +
" By sharing Ivy Wallet you'll make two developers happy and also help a friend to take control of their finances.",
" By sharing Ivy Wallet you'll make two developers happy and also help a friend to take control of their finances.",
cta = "Share with friends",
ctaIcon = R.drawable.ic_custom_family_s,
backgroundColor = Red3,
Expand All @@ -189,7 +191,7 @@ class CustomerJourneyLogic(
},
title = "Lifetime Premium",
description = "We understand that owning something is better than just paying a subscription for it." +
" That's why we've included this special limited lifetime offer only for our best users like you.",
" That's why we've included this special limited lifetime offer only for our best users like you.",
cta = "Get Lifetime Premium",
ctaIcon = R.drawable.ic_custom_crown_s,
backgroundColor = Ivy,
Expand All @@ -206,8 +208,8 @@ class CustomerJourneyLogic(
},
title = "Did you know?",
description = "You can generate reports to get deep insights about your income and spending." +
" Filter your transactions by type, time period, category, accounts, amount, keywords and more" +
" to gain better view on your finances.",
" Filter your transactions by type, time period, category, accounts, amount, keywords and more" +
" to gain better view on your finances.",
cta = "Make a report",
ctaIcon = R.drawable.ic_statistics_xs,
backgroundColor = Green2,
Expand All @@ -224,9 +226,9 @@ class CustomerJourneyLogic(
},
title = "Review Ivy Wallet",
description = "Want to make Ivy Wallet better? Write us a review." +
" That's the only way for us to develop what you want and need." +
" Also it help us rank higher in the PlayStore so we can spend money on the product rather than marketing." +
"\n\nWe do our best.\nIvy Team",
" That's the only way for us to develop what you want and need." +
" Also it help us rank higher in the PlayStore so we can spend money on the product rather than marketing." +
"\n\nWe do our best.\nIvy Team",
cta = "Rate us on Google Play",
ctaIcon = R.drawable.ic_custom_star_s,
backgroundColor = GreenLight,
Expand All @@ -243,10 +245,10 @@ class CustomerJourneyLogic(
},
title = "We need your help!",
description = "We're just a designer and a developer" +
" working on the app after our 9-5 jobs. Currently, we invest a lot of time and money" +
" to generate only losses and exhaustion." +
" If you want us to keep developing Ivy Wallet please share it with friends and family." +
"\n\nP.S. Google PlayStore reviews also helps a lot!",
" working on the app after our 9-5 jobs. Currently, we invest a lot of time and money" +
" to generate only losses and exhaustion." +
" If you want us to keep developing Ivy Wallet please share it with friends and family." +
"\n\nP.S. Google PlayStore reviews also helps a lot!",
cta = "Share Ivy Wallet",
ctaIcon = R.drawable.ic_custom_family_s,
backgroundColor = Purple2,
Expand All @@ -255,6 +257,24 @@ class CustomerJourneyLogic(
ivyActivity.shareIvyWallet()
}
)

fun ivyWalletIsOpenSource() = CustomerJourneyCardData(
id = "open_source",
condition = { trnCount, _, _ ->
trnCount >= 28
},
title = "Ivy Wallet is open-source!",
description = "Ivy Wallet's code is open and everyone can see it." +
" We believe that transparency and ethics are must for every software product." +
" If you like our work and want to make the app better you can contribute in our public Github repository.",
cta = "Contribute",
ctaIcon = R.drawable.github_logo,
backgroundColor = Blue3,
hasDismiss = true,
onAction = { _, ivyActivity ->
ivyActivity.openUrlInBrowser(Constants.URL_IVY_WALLET_REPO)
}
)
}
}

Expand Down Expand Up @@ -390,5 +410,18 @@ private fun PreviewShaveIvyWallet_2() {
}
}

@Preview
@Composable
private fun PreviewIvyWallet_isOpenSource() {
IvyComponentPreview {
CustomerJourneyCard(
cardData = CustomerJourneyLogic.ivyWalletIsOpenSource(),
onCTA = { },
onDismiss = {}
)
}
}




23 changes: 23 additions & 0 deletions app/src/main/java/com/ivy/wallet/network/RestClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.ivy.wallet.network.error.RestError
import com.ivy.wallet.network.service.*
import com.ivy.wallet.session.IvySession
import com.ivy.wallet.session.NoSessionException
import okhttp3.Credentials
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
Expand Down Expand Up @@ -101,6 +102,27 @@ class RestClient private constructor(
response
})

//Github Rest API interceptor (not the best solution)
httpClientBuilder.addInterceptor(Interceptor { chain ->
val request = chain.request()
val finalRequest =
if (request.url.toUrl().toString().startsWith(GithubService.BASE_URL)) {
val credentials = Credentials.basic(
GithubService.GITHUB_SERVICE_ACC_USERNAME,
GithubService.GITHUB_SERVICE_ACC_ACCESS_TOKEN_PART_1 +
GithubService.GITHUB_SERVICE_ACC_ACCESS_TOKEN_PART_2
)

request.newBuilder()
.header("Authorization", credentials)
.build()
} else {
request
}

chain.proceed(request = finalRequest)
})

trustAllSSLCertificates(httpClientBuilder)

return Retrofit.Builder()
Expand Down Expand Up @@ -187,4 +209,5 @@ class RestClient private constructor(
val bankIntegrationsService: BankIntegrationsService by lazy {
retrofit.create(BankIntegrationsService::class.java)
}
val githubService: GithubService by lazy { retrofit.create(GithubService::class.java) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.ivy.wallet.network.request.github

data class OpenIssueRequest(
val title: String,
val body: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.ivy.wallet.network.request.github

data class OpenIssueResponse(
val url: String
)
27 changes: 27 additions & 0 deletions app/src/main/java/com/ivy/wallet/network/service/GithubService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.ivy.wallet.network.service

import com.ivy.wallet.network.request.github.OpenIssueRequest
import com.ivy.wallet.network.request.github.OpenIssueResponse
import retrofit2.http.Body
import retrofit2.http.Header
import retrofit2.http.POST

interface GithubService {
companion object {
const val BASE_URL = "https://api.github.com"
const val OPEN_ISSUE_URL = "$BASE_URL/repos/ILIYANGERMANOV/ivy-wallet/issues"

const val GITHUB_SERVICE_ACC_USERNAME = "ivywallet"

//Split Github Access token in two parts so Github doesn't delete it
//because "Personal access token was found in commit."
const val GITHUB_SERVICE_ACC_ACCESS_TOKEN_PART_1 = "ghp_MuvrbtIH897"
const val GITHUB_SERVICE_ACC_ACCESS_TOKEN_PART_2 = "JASL6i8mBvXJ3aM7DLk4U9Gwq"
}

@POST(OPEN_ISSUE_URL)
suspend fun openIssue(
@Header("Accept") accept: String = "application/vnd.github.v3+json",
@Body request: OpenIssueRequest
): OpenIssueResponse
}
38 changes: 29 additions & 9 deletions app/src/main/java/com/ivy/wallet/ui/IvyActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,12 @@ class IvyActivity : AppCompatActivity() {
// Make the app drawing area fullscreen (draw behind status and nav bars)
WindowCompat.setDecorFitsSystemWindows(window, false)

ivyContext.onShowDatePicker = { minDate, maxDate, onDatePicked ->
ivyContext.onShowDatePicker = { minDate,
maxDate,
initialDate,
onDatePicked ->
val picker = android.app.DatePickerDialog(this)

if (minDate != null) {
picker.datePicker.minDate = minDate.atTime(12, 0).toEpochMilli()
}
Expand All @@ -135,6 +139,15 @@ class IvyActivity : AppCompatActivity() {
onDatePicked(LocalDate.of(year, month + 1, dayOfMonth))
}
picker.show()

if (initialDate != null) {
picker.updateDate(
initialDate.year,
//month-1 because LocalDate start from 1 and date picker starts from 0
initialDate.monthValue - 1,
initialDate.dayOfMonth
)
}
}

ivyContext.onShowTimePicker = { onTimePicked ->
Expand Down Expand Up @@ -429,7 +442,7 @@ class IvyActivity : AppCompatActivity() {
putExtra(Intent.EXTRA_EMAIL, arrayOf(SUPPORT_EMAIL))
putExtra(
Intent.EXTRA_SUBJECT, "Ivy Wallet Support Request #" + caseNumber +
"0" + BuildConfig.VERSION_CODE
"0" + BuildConfig.VERSION_CODE
)
putExtra(Intent.EXTRA_TEXT, "")
}
Expand Down Expand Up @@ -484,13 +497,20 @@ class IvyActivity : AppCompatActivity() {
startActivity(intent)
}

fun openCalculatorApp() {
//TODO: It doesn't work better implement our own calculator
// val intent = Intent().apply {
// action = Intent.ACTION_MAIN
// addCategory(Intent.CATEGORY_APP_CALCULATOR)
// }
// startActivity(intent)
private fun openUrlInDefaultBrowser(url: String) {
try {
val browserIntent = Intent(Intent.ACTION_VIEW)
browserIntent.data = Uri.parse(url)
startActivity(browserIntent)
} catch (e: Exception) {
e.printStackTrace()
e.sendToCrashlytics("Cannot open URL in browser, intent not supported.")
Toast.makeText(
this,
"No browser app found. Visit manually: $url",
Toast.LENGTH_LONG
).show()
}
}

fun reviewIvyWallet(dismissReviewCard: Boolean) {
Expand Down
10 changes: 8 additions & 2 deletions app/src/main/java/com/ivy/wallet/ui/IvyContext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,22 @@ class IvyContext {
//------------------------------------------- BackStack ----------------------------------------

//Activity help -------------------------------------------------------------------------------
lateinit var onShowDatePicker: (minDate: LocalDate?, maxDate: LocalDate?, onDatePicked: (LocalDate) -> Unit) -> Unit
lateinit var onShowDatePicker: (
minDate: LocalDate?,
maxDate: LocalDate?,
initialDate: LocalDate?,
onDatePicked: (LocalDate) -> Unit
) -> Unit
lateinit var onShowTimePicker: (onDatePicked: (LocalTime) -> Unit) -> Unit
lateinit var onContactSupport: () -> Unit

fun datePicker(
minDate: LocalDate? = null,
maxDate: LocalDate? = null,
initialDate: LocalDate?,
onDatePicked: (LocalDate) -> Unit
) {
onShowDatePicker(minDate, maxDate, onDatePicked)
onShowDatePicker(minDate, maxDate, initialDate, onDatePicked)
}

fun timePicker(onTimePicked: (LocalTime) -> Unit) {
Expand Down
Loading

0 comments on commit f9432de

Please sign in to comment.