Skip to content

Commit 1aa9967

Browse files
author
Kasim Rangwala
committed
[ADD] option for self-signed cert in configs.xml
[IMP] added network log along with odoo debug trace in Prompt Report dialog [IMP] other minor improvements
1 parent c9a1dc4 commit 1aa9967

File tree

14 files changed

+184
-33
lines changed

14 files changed

+184
-33
lines changed

.idea/caches/build_file_checksums.ser

0 Bytes
Binary file not shown.

Odoo.JsonRpc.Client.1.04.apk

-2.57 MB
Binary file not shown.

Odoo.JsonRpc.Client.1.041.apk

2.58 MB
Binary file not shown.

app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ android {
2828
applicationId "io.gripxtech.odoojsonrpcclient"
2929
minSdkVersion 17
3030
targetSdkVersion 28
31-
versionCode 5
32-
versionName "1.04"
31+
versionCode 6
32+
versionName "1.041"
3333
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
3434
vectorDrawables.useSupportLibrary = true
3535

app/src/main/java/io/gripxtech/odoojsonrpcclient/Utils.kt

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,24 @@ import androidx.appcompat.app.AlertDialog
2020
import androidx.appcompat.app.AppCompatActivity
2121
import androidx.core.app.ActivityCompat
2222
import androidx.core.app.TaskStackBuilder
23+
import androidx.core.content.FileProvider
2324
import com.google.gson.*
2425
import io.gripxtech.odoojsonrpcclient.core.Odoo
2526
import io.gripxtech.odoojsonrpcclient.core.OdooUser
2627
import io.gripxtech.odoojsonrpcclient.core.authenticator.SplashActivity
2728
import io.gripxtech.odoojsonrpcclient.core.entities.Many2One
2829
import io.gripxtech.odoojsonrpcclient.core.entities.odooError.OdooError
2930
import io.gripxtech.odoojsonrpcclient.core.entities.session.authenticate.AuthenticateResult
31+
import io.gripxtech.odoojsonrpcclient.core.utils.Retrofit2Helper
32+
import io.gripxtech.odoojsonrpcclient.core.utils.android.ktx.subscribeEx
3033
import io.gripxtech.odoojsonrpcclient.core.utils.decryptAES
3134
import io.gripxtech.odoojsonrpcclient.core.utils.encryptAES
35+
import io.reactivex.Completable
36+
import io.reactivex.android.schedulers.AndroidSchedulers
37+
import io.reactivex.schedulers.Schedulers
3238
import retrofit2.Response
3339
import java.io.ByteArrayOutputStream
40+
import java.io.File
3441
import java.text.SimpleDateFormat
3542
import java.util.*
3643

@@ -255,22 +262,55 @@ fun AppCompatActivity.promptReport(odooError: OdooError) {
255262
showNeutralButton = true,
256263
neutralButton = getString(R.string.error_report),
257264
neutralButtonListener = DialogInterface.OnClickListener { _, _ ->
258-
val intent = emailIntent(
259-
address = arrayOf(getString(R.string.preference_contact_summary)),
260-
cc = arrayOf(),
261-
subject = "${getString(R.string.app_name)} ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE}) " +
262-
getString(R.string.report_feedback),
263-
body = "Name: ${odooError.data.name}\n\n" +
264-
"Message: ${odooError.data.message}\n\n" +
265-
"Exception Type: ${odooError.data.exceptionType}\n\n" +
266-
"Arguments: ${odooError.data.arguments}\n\n" +
267-
"Debug: ${odooError.data.debug}\n\n"
268-
)
269-
try {
270-
startActivity(intent)
271-
} catch (e: Exception) {
272-
e.printStackTrace()
273-
showMessage(message = getString(R.string.preference_error_email_intent))
265+
val debugTraceFile = "debug-trace.txt"
266+
Completable.fromCallable {
267+
openFileOutput(debugTraceFile, Context.MODE_PRIVATE)?.use {
268+
it.write(
269+
("Name: ${odooError.data.name}\n\n" +
270+
"Message: ${odooError.data.message}\n\n" +
271+
"Exception Type: ${odooError.data.exceptionType}\n\n" +
272+
"Arguments: ${odooError.data.arguments}\n\n" +
273+
"Debug: ${odooError.data.debug}\n\n").toByteArray(charset = Charsets.UTF_8)
274+
)
275+
}
276+
}.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribeEx {
277+
onSubscribe { }
278+
279+
onError {
280+
it.printStackTrace()
281+
}
282+
283+
onComplete {
284+
val logFileUri = Retrofit2Helper.getLogfile()
285+
val debugTraceUri = FileProvider.getUriForFile(
286+
this@promptReport,
287+
"$packageName.fileprovider",
288+
File("$filesDir${File.pathSeparator}$debugTraceFile")
289+
)
290+
startActivity(
291+
Intent.createChooser(
292+
Intent(Intent.ACTION_SEND_MULTIPLE).apply {
293+
type = "text/plain"
294+
putExtra(Intent.EXTRA_EMAIL, arrayOf(getString(R.string.preference_contact_summary)))
295+
putExtra(
296+
Intent.EXTRA_SUBJECT,
297+
"${getString(R.string.app_name)} ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE}) " +
298+
getString(R.string.report_feedback)
299+
)
300+
putExtra(
301+
Intent.EXTRA_TEXT,
302+
"Name: ${odooError.data.name}\n\n" +
303+
"Message: ${odooError.data.message}\n\n" +
304+
"Exception Type: ${odooError.data.exceptionType}\n\n" +
305+
"Arguments: ${odooError.data.arguments}\n\n"
306+
)
307+
putParcelableArrayListExtra(Intent.EXTRA_STREAM, arrayListOf(debugTraceUri, logFileUri))
308+
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
309+
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
310+
}, getString(R.string.preference_logfile_title)
311+
)
312+
)
313+
}
274314
}
275315
}
276316
)

app/src/main/java/io/gripxtech/odoojsonrpcclient/core/preferences/SettingsFragment.kt

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.gripxtech.odoojsonrpcclient.core.preferences
22

3+
import android.content.ClipData
34
import android.content.Intent
45
import android.net.MailTo
56
import android.net.Uri
@@ -12,10 +13,12 @@ import com.google.android.material.snackbar.Snackbar
1213
import io.gripxtech.odoojsonrpcclient.*
1314
import io.gripxtech.odoojsonrpcclient.core.authenticator.SplashActivity
1415
import io.gripxtech.odoojsonrpcclient.core.utils.LocalePrefs
16+
import io.gripxtech.odoojsonrpcclient.core.utils.Retrofit2Helper
1517
import io.gripxtech.odoojsonrpcclient.core.utils.android.ktx.subscribeEx
1618
import io.reactivex.Single
1719
import io.reactivex.android.schedulers.AndroidSchedulers
1820
import io.reactivex.schedulers.Schedulers
21+
import timber.log.Timber
1922

2023
class SettingsFragment : PreferenceFragmentCompat() {
2124

@@ -27,6 +30,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
2730

2831
private lateinit var build: Preference
2932
private lateinit var language: ListPreference
33+
private lateinit var logfile: Preference
3034
private lateinit var organization: Preference
3135
private lateinit var privacy: Preference
3236
private lateinit var contact: Preference
@@ -38,6 +42,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
3842

3943
build = findPreference(getString(R.string.preference_build_key))
4044
language = findPreference(getString(R.string.preference_language_key)) as ListPreference
45+
logfile = findPreference(getString(R.string.preference_logfile_key))
4146
organization = findPreference(getString(R.string.preference_organization_key))
4247
privacy = findPreference(getString(R.string.preference_privacy_policy_key))
4348
contact = findPreference(getString(R.string.preference_contact_key))
@@ -63,6 +68,29 @@ class SettingsFragment : PreferenceFragmentCompat() {
6368
true
6469
}
6570

71+
logfile.setOnPreferenceClickListener {
72+
val logFileUri = Retrofit2Helper.getLogfile()
73+
activity.startActivity(
74+
Intent.createChooser(
75+
Intent(Intent.ACTION_SEND).apply {
76+
type = "text/plain"
77+
putExtra(Intent.EXTRA_EMAIL, arrayOf(getString(R.string.preference_contact_summary)))
78+
putExtra(
79+
Intent.EXTRA_SUBJECT,
80+
"Contact by ${getString(R.string.app_name)} user: ${activity.getActiveOdooUser()?.name
81+
?: "N/A"}"
82+
)
83+
putExtra(Intent.EXTRA_TEXT, "${getString(R.string.preference_logfile)}\n")
84+
putExtra(Intent.EXTRA_STREAM, logFileUri)
85+
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
86+
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
87+
clipData = ClipData.newRawUri(Retrofit2Helper.networkLogFile, logFileUri)
88+
}, getString(R.string.preference_logfile_title)
89+
)
90+
)
91+
true
92+
}
93+
6694
organization.setOnPreferenceClickListener {
6795
startActivity(
6896
Intent(
@@ -86,12 +114,8 @@ class SettingsFragment : PreferenceFragmentCompat() {
86114
contact.setOnPreferenceClickListener {
87115
val lclContext = context
88116
val url = ("mailto:" + getString(R.string.preference_contact_summary)
89-
+ "?subject=Contact by " + getString(R.string.app_name) + " user " +
90-
if (lclContext != null && lclContext.getActiveOdooUser() != null) {
91-
lclContext.getActiveOdooUser()!!.name
92-
} else {
93-
"N/A"
94-
})
117+
+ "?subject=Contact by " + getString(R.string.app_name) + " user "
118+
+ (lclContext?.getActiveOdooUser()?.name ?: "N/A"))
95119
try {
96120
val mt = MailTo.parse(url)
97121
val i = emailIntent(arrayOf(mt.to), arrayOf(), mt.subject, "")

app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/Retrofit2Helper.kt

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
11
package io.gripxtech.odoojsonrpcclient.core.utils
22

3+
import android.content.Context
4+
import android.net.Uri
5+
import androidx.core.content.FileProvider
36
import io.gripxtech.odoojsonrpcclient.App
47
import io.gripxtech.odoojsonrpcclient.R
58
import io.gripxtech.odoojsonrpcclient.core.Odoo
9+
import io.gripxtech.odoojsonrpcclient.core.utils.android.ktx.subscribeEx
610
import io.gripxtech.odoojsonrpcclient.gson
11+
import io.reactivex.Completable
12+
import io.reactivex.android.schedulers.AndroidSchedulers
13+
import io.reactivex.schedulers.Schedulers
714
import okhttp3.*
815
import okhttp3.logging.HttpLoggingInterceptor
916
import retrofit2.Retrofit
1017
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
1118
import retrofit2.converter.gson.GsonConverterFactory
1219
import timber.log.Timber
20+
import java.io.File
1321
import java.security.SecureRandom
1422
import java.security.cert.X509Certificate
23+
import java.util.*
1524
import javax.net.ssl.SSLContext
1625
import javax.net.ssl.TrustManager
1726
import javax.net.ssl.X509TrustManager
@@ -23,6 +32,14 @@ class Retrofit2Helper(
2332
) {
2433
companion object {
2534
const val TAG = "Retrofit2Helper"
35+
const val networkLogFile = "network_log_file.txt"
36+
37+
fun getLogfile(): Uri {
38+
val file = File("${app.filesDir}${File.pathSeparator}$networkLogFile")
39+
return FileProvider.getUriForFile(
40+
app, "${app.packageName}.fileprovider", file
41+
)
42+
}
2643

2744
enum class Protocol {
2845
HTTP, HTTPS
@@ -82,34 +99,40 @@ class Retrofit2Helper(
8299
private var cookies: MutableList<Cookie>? = Retrofit2Helper.app.cookiePrefs.getCookies()
83100

84101
override fun saveFromResponse(url: HttpUrl?, cookies: MutableList<Cookie>?) {
85-
if (url.toString().contains("/web/session/authenticate") || url.toString().contains("web/session/check")) {
102+
if (cookies != null && cookies.isNotEmpty()) {
86103
this.cookies = cookies
87-
if (cookies != null) {
88-
Odoo.pendingAuthenticateCookies.clear()
89-
Odoo.pendingAuthenticateCookies.addAll(cookies)
90-
}
104+
Odoo.pendingAuthenticateCookies.clear()
105+
Odoo.pendingAuthenticateCookies.addAll(cookies)
91106
}
92107
}
93108

94109
override fun loadForRequest(url: HttpUrl?): MutableList<Cookie>? =
95110
cookies
96111
})
97112
.addInterceptor { chain: Interceptor.Chain? ->
113+
writeFile(dateStamp, Context.MODE_PRIVATE)
98114
val original = chain!!.request()
99115

100116
val request = original.newBuilder()
101117
.header("User-Agent", android.os.Build.MODEL)
102118
.method(original.method(), original.body())
103119
.build()
104120

105-
chain.proceed(request)
121+
chain.proceed(request).also {
122+
writeFile(dateStamp, Context.MODE_APPEND)
123+
}
106124
}
107125
.addInterceptor(HttpLoggingInterceptor {
108126
Timber.tag("OkHttp").d(it)
127+
writeFile("$it\n", Context.MODE_APPEND)
109128
}.apply {
110129
level = HttpLoggingInterceptor.Level.BODY
111130
})
112-
/*.apply(::unsafeCert)*/
131+
.apply {
132+
if (app.resources.getBoolean(R.bool.self_signed_cert)) {
133+
unsafeCert(this)
134+
}
135+
}
113136
.build()
114137

115138
private fun unsafeCert(builder: OkHttpClient.Builder) {
@@ -129,4 +152,49 @@ class Retrofit2Helper(
129152
builder.sslSocketFactory(sslSocketFactory, trustManagers[0] as X509TrustManager)
130153
builder.hostnameVerifier { _, _ -> true }
131154
}
155+
156+
private val dateStamp: String
157+
get() = "------------------------- ${Date()}: -------------------------\n"
158+
159+
private var logOperationRunning = false
160+
private var logOperationParams = ArrayList<Pair<String, Int>>()
161+
162+
private fun writeFile(fileContents: String, mode: Int, skipCheck: Boolean = false) {
163+
if (skipCheck || !logOperationRunning) {
164+
logOperationRunning = true
165+
Completable.fromCallable {
166+
app.openFileOutput(networkLogFile, mode)?.use {
167+
if (fileContents.startsWith("Set-Cookie:")) {
168+
it.write(fileContents.encryptAES().toByteArray(charset = Charsets.UTF_8))
169+
} else {
170+
it.write(fileContents.toByteArray(charset = Charsets.UTF_8))
171+
}
172+
}
173+
}.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribeEx {
174+
175+
fun popFirstParams() {
176+
if (logOperationParams.isNotEmpty()) {
177+
logOperationParams.removeAt(0).let {
178+
writeFile(it.first, it.second, true)
179+
}
180+
} else {
181+
logOperationRunning = false
182+
}
183+
}
184+
185+
onSubscribe { }
186+
187+
onError {
188+
it.printStackTrace()
189+
popFirstParams()
190+
}
191+
192+
onComplete {
193+
popFirstParams()
194+
}
195+
}
196+
} else {
197+
logOperationParams.add(Pair(fileContents, mode))
198+
}
199+
}
132200
}

app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/android/ktx/MaybeObserverEx.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class MaybeObserverEx<T> : MaybeObserver<T> {
1919
}
2020

2121
private var success: ((response: T) -> Unit) = {
22-
Timber.d("onNext() called: response is $it")
22+
Timber.d("onSuccess() called: response is $it")
2323
}
2424

2525
fun onSuccess(success: (response: T) -> Unit) {

app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/android/ktx/SingleObserverEx.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class SingleObserverEx<T> : SingleObserver<T> {
1919
}
2020

2121
private var success: ((response: T) -> Unit) = {
22-
Timber.d("onNext() called: response is $it")
22+
Timber.d("onSuccess() called: response is $it")
2323
}
2424

2525
fun onSuccess(success: (response: T) -> Unit) {

app/src/main/res/values-ar/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@
6464
<string name="preference_build_summary">الإصدار %s</string>
6565
<string name="preference_language_title">لغة</string>
6666
<string name="preference_language_summary">اختر لغتك</string>
67+
<string name="preference_logfile_title">مشاركة Logfile التطبيق</string>
68+
<string name="preference_logfile">تطبيق logfile</string>
6769
<string name="preference_about_category">حول</string>
6870
<string name="preference_organization_title">طورت بواسطة</string>
6971
<string name="preference_privacy_policy_title">سياسة خاصة</string>

0 commit comments

Comments
 (0)