diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser
index e554c5e..f169266 100644
Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 30aa626..34dc27c 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -1,5 +1,8 @@
+
+
+
@@ -25,5 +28,8 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..79ee123
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 99202cc..af0bbdd 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,29 +1,9 @@
-
-
-
-
-
+
+
+
+
diff --git a/README.md b/README.md
index b096ac1..27eb6ec 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ Update [`tokenKey`](https://github.com/kasim1011/OdooJsonRpcClient/blob/5edf9e5e
Do not hesitate to report [issues](https://github.com/kasim1011/OdooJsonRpcClient/issues) you may find.
-Get the **sample APK** from [release](https://github.com/kasim1011/OdooJsonRpcClient/releases) saction.
+Get the **sample APK** from [release](https://github.com/kasim1011/OdooJsonRpcClient/releases) section.
Next Milestone:
- **Synchronization** and **Persistence** using [Room Persistence Library](https://developer.android.com/topic/libraries/architecture/room)
@@ -288,7 +288,7 @@ Odoo.searchRead(model = "res.partner", fields = listOf(
val searchRead = response.body()!!
if (searchRead.isSuccessful) {
val result = searchRead.result
- // use gson to convert records (jsonArray) to list of pojo
+ // use gson to convert records (jsonArray) to list of POJO
// ...
} else {
// Odoo specific error
diff --git a/app/build.gradle b/app/build.gradle
index b20fddd..e2ea9f6 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -2,7 +2,6 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
-apply plugin: 'io.mironov.smuggler'
// Create a variable called keystorePropertiesFile, and initialize it to your
// keystore.properties file, in the rootProject folder.
@@ -24,15 +23,17 @@ android {
}
}
- compileSdkVersion 27
+ compileSdkVersion 28
defaultConfig {
applicationId "io.gripxtech.odoojsonrpcclient"
minSdkVersion 17
- targetSdkVersion 27
- versionCode 3
- versionName "1.02"
- testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ targetSdkVersion 28
+ versionCode 4
+ versionName "1.03"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
+
+ multiDexEnabled true
}
buildTypes {
release {
@@ -45,6 +46,9 @@ android {
signingConfig signingConfigs.config
}
}
+ androidExtensions {
+ experimental = true
+ }
dataBinding {
enabled true
}
@@ -56,30 +60,29 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- implementation "com.android.support:appcompat-v7:$support_library_version"
- implementation "com.android.support:design:$support_library_version"
- implementation "com.android.support:recyclerview-v7:$support_library_version"
- implementation "com.android.support:cardview-v7:$support_library_version"
- implementation "com.android.support:preference-v14:$support_library_version"
- implementation 'com.android.support.constraint:constraint-layout:1.1.2'
- implementation 'com.android.support:support-v4:27.1.1'
- kapt "com.android.databinding:compiler:$gradle_version"
+ implementation 'androidx.appcompat:appcompat:1.1.0-alpha01'
+ implementation 'com.google.android.material:material:1.1.0-alpha02'
+ implementation 'androidx.recyclerview:recyclerview:1.1.0-alpha01'
+ implementation 'androidx.cardview:cardview:1.0.0'
+ implementation 'androidx.preference:preference-ktx:1.1.0-alpha02'
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha3'
+ implementation 'androidx.multidex:multidex:2.0.1'
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofit_version"
implementation "com.github.bumptech.glide:glide:$glide_version"
kapt "com.github.bumptech.glide:compiler:$glide_version"
implementation "com.github.bumptech.glide:okhttp3-integration:$glide_version"
- implementation 'de.hdodenhof:circleimageview:2.2.0'
implementation 'com.google.code.gson:gson:2.8.5'
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
implementation "com.squareup.okhttp3:logging-interceptor:$okhttp_version"
- implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
- implementation 'io.reactivex.rxjava2:rxjava:2.1.16'
- implementation 'com.jakewharton.timber:timber:4.7.0'
- implementation 'com.intuit.sdp:sdp-android:1.0.5'
- implementation 'com.intuit.ssp:ssp-android:1.0.5'
+ implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
+ implementation 'io.reactivex.rxjava2:rxjava:2.2.6'
+ implementation 'de.hdodenhof:circleimageview:3.0.0'
+ implementation 'com.jakewharton.timber:timber:4.7.1'
+ implementation 'com.intuit.sdp:sdp-android:1.0.6'
+ implementation 'com.intuit.ssp:ssp-android:1.0.6'
testImplementation 'junit:junit:4.12'
- androidTestImplementation 'com.android.support.test:runner:1.0.2'
- androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+ androidTestImplementation 'androidx.test:runner:1.1.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 54bce4c..03b5295 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -59,6 +59,7 @@
# Application classes that will be serialized/deserialized over Gson
-keep class io.gripxtech.odoojsonrpcclient.core.entities.** { *; }
-keep interface io.gripxtech.odoojsonrpcclient.core.web.** { *; }
+-keep class io.gripxtech.odoojsonrpcclient.customer.entities.** { *; }
# Prevent proguard from stripping interface information from TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
diff --git a/app/src/androidTest/java/io/gripxtech/odoojsonrpcclient/ExampleInstrumentedTest.kt b/app/src/androidTest/java/io/gripxtech/odoojsonrpcclient/ExampleInstrumentedTest.kt
index 4488469..8d9452b 100644
--- a/app/src/androidTest/java/io/gripxtech/odoojsonrpcclient/ExampleInstrumentedTest.kt
+++ b/app/src/androidTest/java/io/gripxtech/odoojsonrpcclient/ExampleInstrumentedTest.kt
@@ -1,13 +1,11 @@
package io.gripxtech.odoojsonrpcclient
-import android.support.test.InstrumentationRegistry
-import android.support.test.runner.AndroidJUnit4
-
+import androidx.test.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
+import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.Assert.*
-
/**
* Instrumented test, which will execute on an Android device.
*
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 8903039..0f65bda 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,76 +1,90 @@
+ xmlns:tools="http://schemas.android.com/tools"
+ package="io.gripxtech.odoojsonrpcclient">
-
-
-
+
+
+
-
-
+ android:name="android.permission.GET_ACCOUNTS"
+ android:maxSdkVersion="22"/>
+
+
+
-
+ android:largeHeap="true"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme"
+ android:usesCleartextTraffic="true"
+ tools:ignore="AllowBackup,GoogleAppIndexingWarning,UnusedAttribute">
+ android:name=".MainActivity"
+ android:label="@string/app_name"
+ android:screenOrientation="portrait"
+ android:theme="@style/AppTheme.NoActionBar"/>
+ android:name=".core.authenticator.LoginActivity"
+ android:label="@string/app_name"
+ android:screenOrientation="portrait"
+ android:theme="@style/AppTheme.NoActionBar"/>
+
-
+
-
+
+ android:name=".core.authenticator.ProfileActivity"
+ android:label="@string/action_profile"
+ android:screenOrientation="portrait"
+ android:theme="@style/AppTheme.NoActionBar"/>
+ android:name=".core.authenticator.ManageAccountActivity"
+ android:label="@string/action_manage_account"
+ android:screenOrientation="portrait"
+ android:theme="@style/AppTheme.NoActionBar"/>
+ android:name=".core.preferences.SettingsActivity"
+ android:label="@string/action_settings"
+ android:screenOrientation="portrait"
+ android:theme="@style/AppTheme.NoActionBar"/>
+ android:name=".core.authenticator.AuthenticatorService"
+ android:enabled="true"
+ android:exported="true"
+ tools:ignore="ExportedService">
-
+
+ android:name="android.accounts.AccountAuthenticator"
+ android:resource="@xml/authenticator"/>
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/App.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/App.kt
index fea248e..7be02e3 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/App.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/App.kt
@@ -1,13 +1,16 @@
package io.gripxtech.odoojsonrpcclient
-import android.app.Application
+import android.content.Context
+import android.content.res.Configuration
+import androidx.multidex.MultiDexApplication
import io.gripxtech.odoojsonrpcclient.core.Odoo
import io.gripxtech.odoojsonrpcclient.core.utils.CookiePrefs
import io.gripxtech.odoojsonrpcclient.core.utils.LetterTileProvider
+import io.gripxtech.odoojsonrpcclient.core.utils.LocaleHelper
import io.gripxtech.odoojsonrpcclient.core.utils.Retrofit2Helper
import timber.log.Timber
-class App : Application() {
+class App : MultiDexApplication() {
companion object {
const val KEY_ACCOUNT_TYPE = "${BuildConfig.APPLICATION_ID}.auth"
@@ -21,10 +24,23 @@ class App : Application() {
CookiePrefs(this)
}
+ override fun attachBaseContext(base: Context?) {
+ if (base != null) {
+ super.attachBaseContext(LocaleHelper.setLocale(base))
+ } else {
+ super.attachBaseContext(base)
+ }
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration?) {
+ super.onConfigurationChanged(newConfig)
+ LocaleHelper.setLocale(this)
+ }
+
override fun onCreate() {
super.onCreate()
- Odoo.app = this
Retrofit2Helper.app = this
+ Odoo.app = this
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
@@ -32,5 +48,5 @@ class App : Application() {
}
fun getLetterTile(displayName: String): ByteArray =
- letterTileProvider.getLetterTile(displayName)
+ letterTileProvider.getLetterTile(displayName)
}
\ No newline at end of file
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/MainActivity.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/MainActivity.kt
index b672142..1e2926d 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/MainActivity.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/MainActivity.kt
@@ -1,17 +1,16 @@
package io.gripxtech.odoojsonrpcclient
+import android.content.Context
import android.content.Intent
import android.content.res.Configuration
-import android.databinding.DataBindingUtil
import android.os.Bundle
-import android.support.v4.view.GravityCompat
-import android.support.v7.app.ActionBarDrawerToggle
-import android.support.v7.app.AppCompatActivity
-import android.support.v7.app.AppCompatDelegate
-import io.gripxtech.odoojsonrpcclient.core.authenticator.LoginActivity
-import io.gripxtech.odoojsonrpcclient.core.authenticator.ManageAccountActivity
-import io.gripxtech.odoojsonrpcclient.core.authenticator.ProfileActivity
+import androidx.appcompat.app.ActionBarDrawerToggle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.app.AppCompatDelegate
+import androidx.core.view.GravityCompat
+import androidx.databinding.DataBindingUtil
import io.gripxtech.odoojsonrpcclient.core.preferences.SettingsActivity
+import io.gripxtech.odoojsonrpcclient.core.utils.LocaleHelper
import io.gripxtech.odoojsonrpcclient.core.utils.NavHeaderViewHolder
import io.gripxtech.odoojsonrpcclient.core.utils.android.ktx.postEx
import io.gripxtech.odoojsonrpcclient.customer.CustomerFragment
@@ -35,7 +34,6 @@ class MainActivity : AppCompatActivity() {
private lateinit var navHeader: NavHeaderViewHolder
private var currentDrawerItemID: Int = 0
- private var drawerClickStatus: Boolean = false
private val customerFragment: CustomerFragment by lazy {
CustomerFragment.newInstance(CustomerFragment.Companion.CustomerType.Customer)
@@ -49,6 +47,14 @@ class MainActivity : AppCompatActivity() {
CustomerFragment.newInstance(CustomerFragment.Companion.CustomerType.Company)
}
+ override fun attachBaseContext(newBase: Context?) {
+ if (newBase != null) {
+ super.attachBaseContext(LocaleHelper.setLocale(newBase))
+ } else {
+ super.attachBaseContext(newBase)
+ }
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
app = application as App
@@ -70,8 +76,8 @@ class MainActivity : AppCompatActivity() {
setTitle(R.string.app_name)
drawerToggle = ActionBarDrawerToggle(
- this, binding.dl, binding.tb,
- R.string.navigation_drawer_open, R.string.navigation_drawer_close
+ this, binding.dl, binding.tb,
+ R.string.navigation_drawer_open, R.string.navigation_drawer_close
)
binding.dl.addDrawerListener(drawerToggle)
drawerToggle.syncState()
@@ -81,30 +87,10 @@ class MainActivity : AppCompatActivity() {
navHeader = NavHeaderViewHolder(view)
val user = getActiveOdooUser()
if (user != null) {
- navHeader.setUser(user)
+ navHeader.setUser(user, GlideApp.with(this@MainActivity))
}
}
- drawerClickStatus = false
-
- navHeader.menuToggle.setOnClickListener {
- val menu = binding.nv.menu
- if (drawerClickStatus) {
- menu.setGroupVisible(R.id.nav_menu_1, true)
- menu.setGroupVisible(R.id.nav_menu_2, true)
- menu.setGroupVisible(R.id.nav_menu_3, true)
- menu.setGroupVisible(R.id.nav_menu_4, false)
- navHeader.menuToggleImage.setImageResource(R.drawable.ic_arrow_drop_down_white_24dp)
- } else {
- menu.setGroupVisible(R.id.nav_menu_1, false)
- menu.setGroupVisible(R.id.nav_menu_2, false)
- menu.setGroupVisible(R.id.nav_menu_3, false)
- menu.setGroupVisible(R.id.nav_menu_4, true)
- navHeader.menuToggleImage.setImageResource(R.drawable.ic_arrow_drop_up_white_24dp)
- }
- drawerClickStatus = !drawerClickStatus
- }
-
binding.nv.setNavigationItemSelectedListener { item ->
binding.dl.postEx { closeDrawer(GravityCompat.START) }
when (item.itemId) {
@@ -126,28 +112,10 @@ class MainActivity : AppCompatActivity() {
}
true
}
- R.id.nav_profile -> {
- if (getActiveOdooUser() != null) {
- startActivity(Intent(this, ProfileActivity::class.java))
- } else {
- showMessage(message = getString(R.string.error_active_user))
- }
- true
- }
R.id.nav_settings -> {
startActivity(Intent(this, SettingsActivity::class.java))
true
}
- R.id.nav_add_account -> {
- val intent = Intent(this, LoginActivity::class.java)
- intent.putExtra(LoginActivity.FROM_APP_SETTINGS, true)
- startActivity(intent)
- true
- }
- R.id.nav_manage_account -> {
- startActivity(Intent(this, ManageAccountActivity::class.java))
- true
- }
else -> {
true
}
@@ -159,9 +127,10 @@ class MainActivity : AppCompatActivity() {
}
}
- override fun onConfigurationChanged(newConfig: Configuration?) {
+ override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
drawerToggle.onConfigurationChanged(newConfig)
+ LocaleHelper.setLocale(this)
}
private fun loadFragment(currentDrawerItemID: Int) {
@@ -170,21 +139,21 @@ class MainActivity : AppCompatActivity() {
when (currentDrawerItemID) {
ACTION_CUSTOMER -> {
supportFragmentManager
- .beginTransaction()
- .replace(R.id.clMain, customerFragment, getString(R.string.action_customer))
- .commit()
+ .beginTransaction()
+ .replace(R.id.clMain, customerFragment, getString(R.string.action_customer))
+ .commit()
}
ACTION_SUPPLIER -> {
supportFragmentManager
- .beginTransaction()
- .replace(R.id.clMain, supplierFragment, getString(R.string.action_supplier))
- .commit()
+ .beginTransaction()
+ .replace(R.id.clMain, supplierFragment, getString(R.string.action_supplier))
+ .commit()
}
ACTION_COMPANY -> {
supportFragmentManager
- .beginTransaction()
- .replace(R.id.clMain, companyFragment, getString(R.string.action_company))
- .commit()
+ .beginTransaction()
+ .replace(R.id.clMain, companyFragment, getString(R.string.action_company))
+ .commit()
}
}
}
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/Utils.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/Utils.kt
index 94f8424..8b4a561 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/Utils.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/Utils.kt
@@ -5,24 +5,34 @@ import android.accounts.AccountManager
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.drawable.Drawable
import android.net.ConnectivityManager
+import android.net.Uri
import android.os.Build
import android.os.Handler
-import android.support.v4.app.ActivityCompat
-import android.support.v4.app.TaskStackBuilder
-import android.support.v7.app.AlertDialog
-import android.support.v7.app.AppCompatActivity
import android.text.Html
import android.text.Spanned
+import android.util.Base64
import android.view.inputmethod.InputMethodManager
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.app.ActivityCompat
+import androidx.core.app.TaskStackBuilder
import com.google.gson.*
import io.gripxtech.odoojsonrpcclient.core.Odoo
import io.gripxtech.odoojsonrpcclient.core.OdooUser
import io.gripxtech.odoojsonrpcclient.core.authenticator.SplashActivity
import io.gripxtech.odoojsonrpcclient.core.entities.Many2One
+import io.gripxtech.odoojsonrpcclient.core.entities.odooError.OdooError
import io.gripxtech.odoojsonrpcclient.core.entities.session.authenticate.AuthenticateResult
+import io.gripxtech.odoojsonrpcclient.core.utils.decryptAES
import io.gripxtech.odoojsonrpcclient.core.utils.encryptAES
import retrofit2.Response
+import java.io.ByteArrayOutputStream
+import java.text.SimpleDateFormat
+import java.util.*
+
const val RECORD_LIMIT = 10
@@ -87,19 +97,58 @@ fun Context.logoutOdooUser(odooUser: OdooUser) {
accountManager.setUserData(odooUser.account, "active", "false")
}
+fun Context.getCookies(odooUser: OdooUser): String {
+ val accountManager = AccountManager.get(this)
+ return accountManager.getUserData(odooUser.account, "cookies")?.decryptAES() ?: ""
+}
+
+fun Context.setCookies(odooUser: OdooUser, cookiesStr: String) {
+ val accountManager = AccountManager.get(this)
+ accountManager.setUserData(odooUser.account, "cookies", cookiesStr.encryptAES())
+}
+
fun Context.deleteOdooUser(odooUser: OdooUser): Boolean {
val accountManager = AccountManager.get(this)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
accountManager.removeAccountExplicitly(odooUser.account)
} else {
@Suppress("DEPRECATION")
- val result = accountManager.removeAccount(odooUser.account, { _ ->
+ val result = accountManager.removeAccount(odooUser.account, {
}, Handler(this.mainLooper))
result != null && result.result != null && result.result!!
}
}
+fun String.toDate(dateFormat: String = "yyyy-MM-dd HH:mm:ss"): Date {
+ val parser = SimpleDateFormat(dateFormat, Locale.US)
+ return parser.parse(this)
+}
+
+fun Date.formatTo(dateFormat: String): String {
+ val formatter = SimpleDateFormat(dateFormat, Locale.US)
+ return formatter.format(this)
+}
+
+fun String.toDate(dateFormat: String = "yyyy-MM-dd HH:mm:ss", timeZone: TimeZone = TimeZone.getTimeZone("UTC")): Date {
+ val parser = SimpleDateFormat(dateFormat, Locale.getDefault())
+ parser.timeZone = timeZone
+ return parser.parse(this)
+}
+
+fun Date.formatTo(dateFormat: String, timeZone: TimeZone = TimeZone.getDefault()): String {
+ val formatter = SimpleDateFormat(dateFormat, Locale.getDefault())
+ formatter.timeZone = timeZone
+ return formatter.format(this)
+}
+
+fun Bitmap.toBase64(): String {
+ val byteArrayOutputStream = ByteArrayOutputStream()
+ compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream)
+ val byteArray = byteArrayOutputStream.toByteArray()
+ return Base64.encodeToString(byteArray, Base64.DEFAULT)
+}
+
val JsonElement.isManyToOne: Boolean get() = isJsonArray && asJsonArray.size() == 2
val JsonElement.asManyToOne: Many2One
@@ -109,6 +158,16 @@ val JsonElement.asManyToOne: Many2One
Many2One(JsonArray().apply { add(0); add("") })
}
+fun Many2One.toStringList(): ArrayList = ArrayList().apply {
+ add(id.toString())
+ add(name)
+}
+
+fun ArrayList.toJsonElement(): JsonElement = JsonArray().apply {
+ add(this[0].asInt)
+ add(this[1])
+}
+
val JsonArray.asIntList: List
get() = this.map {
it.asInt
@@ -126,6 +185,8 @@ fun AppCompatActivity.hideSoftKeyboard() {
if (view != null) {
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(view.windowToken, 0)
+
+ view.clearFocus()
}
}
@@ -138,34 +199,95 @@ fun AppCompatActivity.restartApp() {
var alertDialog: AlertDialog? = null
fun AppCompatActivity.showMessage(
- title: CharSequence? = null,
- message: CharSequence?,
- cancelable: Boolean = false,
- positiveButton: CharSequence = getString(R.string.ok),
- positiveButtonListener: DialogInterface.OnClickListener = DialogInterface.OnClickListener { _, _ -> },
- showNegativeButton: Boolean = false,
- negativeButton: CharSequence = getString(R.string.cancel),
- negativeButtonListener: DialogInterface.OnClickListener = DialogInterface.OnClickListener { _, _ -> }
+ title: CharSequence? = null,
+ message: CharSequence?,
+ cancelable: Boolean = false,
+ icon: Drawable? = null,
+ positiveButton: CharSequence = getString(R.string.ok),
+ positiveButtonListener: DialogInterface.OnClickListener? = DialogInterface.OnClickListener { _, _ -> }
+): AlertDialog = showMessage(
+ title, message, cancelable, icon, positiveButton, positiveButtonListener,
+ false, getString(R.string.cancel), DialogInterface.OnClickListener { _, _ -> }
+)
+
+fun AppCompatActivity.showMessage(
+ title: CharSequence? = null,
+ message: CharSequence?,
+ cancelable: Boolean = false,
+ icon: Drawable? = null,
+ positiveButton: CharSequence = getString(R.string.ok),
+ positiveButtonListener: DialogInterface.OnClickListener? = DialogInterface.OnClickListener { _, _ -> },
+ showNegativeButton: Boolean = false,
+ negativeButton: CharSequence = getString(R.string.cancel),
+ negativeButtonListener: DialogInterface.OnClickListener? = DialogInterface.OnClickListener { _, _ -> },
+ showNeutralButton: Boolean = false,
+ neutralButton: CharSequence = getString(R.string.cancel),
+ neutralButtonListener: DialogInterface.OnClickListener? = DialogInterface.OnClickListener { _, _ -> }
): AlertDialog {
alertDialog?.dismiss()
alertDialog = AlertDialog.Builder(this, R.style.AppAlertDialogTheme)
- .setTitle(title)
- .setMessage(if (message?.isNotEmpty() == true) {
- message
- } else {
- getString(R.string.generic_error)
- })
- .setCancelable(cancelable)
- .setPositiveButton(positiveButton, positiveButtonListener)
- .apply {
- if (showNegativeButton) {
- setNegativeButton(negativeButton, negativeButtonListener)
- }
+ .setTitle(title)
+ .setMessage(if (message?.isNotEmpty() == true) {
+ message
+ } else {
+ getString(R.string.generic_error)
+ })
+ .setCancelable(cancelable)
+ .setIcon(icon)
+ .setPositiveButton(positiveButton, positiveButtonListener)
+ .apply {
+ if (showNegativeButton) {
+ setNegativeButton(negativeButton, negativeButtonListener)
+ }
+ if (showNeutralButton) {
+ setNeutralButton(neutralButton, neutralButtonListener)
}
- .show()
+ }
+ .show()
return alertDialog!!
}
+fun AppCompatActivity.promptReport(odooError: OdooError) {
+ showMessage(
+ message = odooError.data.message,
+ showNeutralButton = true,
+ neutralButton = getString(R.string.error_report),
+ neutralButtonListener = DialogInterface.OnClickListener { _, _ ->
+ val intent = emailIntent(
+ address = arrayOf(getString(R.string.preference_contact_summary)),
+ cc = arrayOf(),
+ subject = "${getString(R.string.app_name)} ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE}) " +
+ getString(R.string.report_feedback),
+ body = "Name: ${odooError.data.name}\n\n" +
+ "Message: ${odooError.data.message}\n\n" +
+ "Exception Type: ${odooError.data.exceptionType}\n\n" +
+ "Arguments: ${odooError.data.arguments}\n\n" +
+ "Debug: ${odooError.data.debug}\n\n"
+ )
+ try {
+ startActivity(intent)
+ } catch (e: Exception) {
+ e.printStackTrace()
+ showMessage(message = getString(R.string.preference_error_email_intent))
+ }
+ }
+ )
+}
+
+fun AppCompatActivity.emailIntent(
+ address: Array,
+ cc: Array,
+ subject: String,
+ body: String
+): Intent {
+ val intent = Intent(Intent.ACTION_VIEW, Uri.parse("mailto:"))
+ intent.putExtra(Intent.EXTRA_EMAIL, address)
+ intent.putExtra(Intent.EXTRA_CC, cc)
+ intent.putExtra(Intent.EXTRA_SUBJECT, subject)
+ intent.putExtra(Intent.EXTRA_TEXT, body)
+ return Intent.createChooser(intent, getString(R.string.preference_prompt_email_intent))
+}
+
@Suppress("DEPRECATION")
fun AppCompatActivity.showServerErrorMessage(
response: Response<*>,
@@ -181,7 +303,13 @@ fun AppCompatActivity.showServerErrorMessage(
)
fun AppCompatActivity.closeApp(message: String = getString(R.string.generic_error)): AlertDialog =
- showMessage(getString(R.string.fatal_error), message, false, getString(R.string.exit), DialogInterface.OnClickListener { _, _ ->
+ showMessage(
+ getString(R.string.fatal_error),
+ message,
+ false,
+ null,
+ getString(R.string.exit),
+ DialogInterface.OnClickListener { _, _ ->
ActivityCompat.finishAffinity(this)
})
@@ -208,7 +336,7 @@ fun AppCompatActivity.isDeviceOnline(): Boolean {
var isConnected = false
val manager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val nInfo = manager.activeNetworkInfo
- if (nInfo != null && nInfo.isConnectedOrConnecting) {
+ if (nInfo != null && nInfo.isConnected) {
isConnected = true
}
return isConnected
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/Odoo.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/Odoo.kt
index e9ce99e..c1fb9cd 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/Odoo.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/Odoo.kt
@@ -95,27 +95,28 @@ object Odoo {
@Suppress("PlatformExtensionReceiverOfInline")
fun fromAccount(manager: AccountManager, account: Account) = OdooUser(
- Retrofit2Helper.Companion.Protocol.valueOf(
- manager.getUserData(account, "protocol")
- ),
- manager.getUserData(account, "host"),
- manager.getUserData(account, "login"),
- manager.getUserData(account, "password").decryptAES(),
- manager.getUserData(account, "database"),
- manager.getUserData(account, "serverVersion"),
- manager.getUserData(account, "isAdmin").toBoolean(),
- manager.getUserData(account, "id").toInt(),
- manager.getUserData(account, "name"),
- manager.getUserData(account, "imageSmall"),
- manager.getUserData(account, "partnerId").toInt(),
- manager.getUserData(account, "context").toJsonObject(),
- manager.getUserData(account, "active").toBoolean(),
- account
+ Retrofit2Helper.Companion.Protocol.valueOf(
+ manager.getUserData(account, "protocol")
+ ),
+ manager.getUserData(account, "host"),
+ manager.getUserData(account, "login"),
+ manager.getUserData(account, "password").decryptAES(),
+ manager.getUserData(account, "database"),
+ manager.getUserData(account, "serverVersion"),
+ manager.getUserData(account, "isAdmin").toBoolean(),
+ manager.getUserData(account, "isSuperuser").toBoolean(),
+ manager.getUserData(account, "id").toInt(),
+ manager.getUserData(account, "name"),
+ manager.getUserData(account, "imageSmall"),
+ manager.getUserData(account, "partnerId").toInt(),
+ manager.getUserData(account, "context").toJsonObject(),
+ manager.getUserData(account, "active").toBoolean(),
+ account
)
private val retrofit2Helper = Retrofit2Helper(
- protocol,
- host
+ protocol,
+ host
)
private val retrofit
get() = retrofit2Helper.retrofit
@@ -136,52 +137,56 @@ object Odoo {
val requestBody = VersionInfoReqBody(id = jsonRpcId)
val observable = request.versionInfo(requestBody)
observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(ResponseObserver().apply(callback))
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(ResponseObserver().apply(callback))
}
fun listDb(serverVersion: String, callback: ResponseObserver.() -> Unit) {
val requestBody = ListDbReqBody(id = jsonRpcId)
val observable =
- when {
- serverVersion.startsWith("8.") -> {
- val request = retrofit.create(ListDbV8Request::class.java)
- request.listDb(requestBody)
- }
- serverVersion.startsWith("9.") -> {
- val request = retrofit.create(ListDbV9Request::class.java)
- request.listDb(requestBody)
- }
- else -> {
- val request = retrofit.create(ListDbRequest::class.java)
- request.listDb(requestBody)
- }
+ when {
+ serverVersion.startsWith("8.") -> {
+ val request = retrofit.create(ListDbV8Request::class.java)
+ request.listDb(requestBody)
+ }
+ serverVersion.startsWith("9.") -> {
+ val request = retrofit.create(ListDbV9Request::class.java)
+ request.listDb(requestBody)
}
+ else -> {
+ val request = retrofit.create(ListDbRequest::class.java)
+ request.listDb(requestBody)
+ }
+ }
observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(ResponseObserver().apply(callback))
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(ResponseObserver().apply(callback))
}
private val pendingAuthenticateCallbacks: ArrayList.() -> Unit> = arrayListOf()
val pendingAuthenticateCookies: ArrayList = arrayListOf()
@Synchronized
- fun authenticate(login: String, password: String, database: String,
- callback: ResponseObserver.() -> Unit) {
+ fun authenticate(
+ login: String, password: String, database: String,
+ callback: ResponseObserver.() -> Unit
+ ) {
pendingAuthenticateCallbacks += callback
if (pendingAuthenticateCallbacks.size == 1) {
val request = retrofit.create(AuthenticateRequest::class.java)
- val requestBody = AuthenticateReqBody(id = jsonRpcId, params = AuthenticateParams(
+ val requestBody = AuthenticateReqBody(
+ id = jsonRpcId, params = AuthenticateParams(
host, login, password, database
- ))
+ )
+ )
val observable = request.authenticate(requestBody)
observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(ResponseObserver().apply {
- (pendingAuthenticateCallbacks.size - 1 downTo 0).map {
- pendingAuthenticateCallbacks.removeAt(it)
- }.forEach { it() }
- })
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(ResponseObserver().apply {
+ (pendingAuthenticateCallbacks.size - 1 downTo 0).map {
+ pendingAuthenticateCallbacks.removeAt(it)
+ }.forEach { it() }
+ })
}
}
@@ -190,8 +195,8 @@ object Odoo {
val requestBody = CheckReqBody(id = jsonRpcId)
val observable = request.check(requestBody)
observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(ResponseObserver().apply(callback))
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(ResponseObserver().apply(callback))
}
fun destroy(callback: ResponseObserver.() -> Unit) {
@@ -199,8 +204,8 @@ object Odoo {
val requestBody = DestroyReqBody(id = jsonRpcId)
val observable = request.destroy(requestBody)
observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(ResponseObserver().apply(callback))
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(ResponseObserver().apply(callback))
}
fun modules(callback: ResponseObserver.() -> Unit) {
@@ -208,8 +213,8 @@ object Odoo {
val requestBody = ModulesReqBody(id = jsonRpcId)
val observable = request.modules(requestBody)
observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(ResponseObserver().apply(callback))
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(ResponseObserver().apply(callback))
}
fun getSessionInfo(callback: ResponseObserver.() -> Unit) {
@@ -217,133 +222,141 @@ object Odoo {
val requestBody = GetSessionInfoReqBody(id = jsonRpcId)
val observable = request.getSessionInfo(requestBody)
observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(ResponseObserver().apply(callback))
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(ResponseObserver().apply(callback))
}
fun searchRead(
- model: String,
- fields: List = listOf(),
- domain: List = listOf(),
- offset: Int = 0,
- limit: Int = 0,
- sort: String = "",
- context: JsonObject = user.context,
- callback: ResponseObserver.() -> Unit
+ model: String,
+ fields: List = listOf(),
+ domain: List = listOf(),
+ offset: Int = 0,
+ limit: Int = 0,
+ sort: String = "",
+ context: JsonObject = user.context,
+ callback: ResponseObserver.() -> Unit
) {
val request = retrofit.create(SearchReadRequest::class.java)
- val requestBody = SearchReadReqBody(id = jsonRpcId, params = SearchReadParams(
+ val requestBody = SearchReadReqBody(
+ id = jsonRpcId, params = SearchReadParams(
model, fields, domain, offset, limit, sort, context
- ))
+ )
+ )
val observable = request.searchRead(requestBody)
observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(ResponseObserver().apply(callback))
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(ResponseObserver().apply(callback))
}
fun load(
- id: Int,
- model: String,
- fields: List = listOf(),
- context: JsonObject = user.context,
- callback: ResponseObserver.() -> Unit
+ id: Int,
+ model: String,
+ fields: List = listOf(),
+ context: JsonObject = user.context,
+ callback: ResponseObserver.() -> Unit
) {
val request = retrofit.create(LoadRequest::class.java)
- val requestBody = LoadReqBody(id = jsonRpcId, params = LoadParams(
+ val requestBody = LoadReqBody(
+ id = jsonRpcId, params = LoadParams(
id, model, fields, context
- ))
+ )
+ )
val observable = request.load(requestBody)
observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(ResponseObserver().apply(callback))
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(ResponseObserver().apply(callback))
}
fun callKw(
- model: String,
- method: String,
- args: List,
- kwArgs: Map = mapOf(),
- context: JsonObject = user.context,
- callback: ResponseObserver.() -> Unit
+ model: String,
+ method: String,
+ args: List,
+ kwArgs: Map = mapOf(),
+ context: JsonObject = user.context,
+ callback: ResponseObserver.() -> Unit
) {
val request = retrofit.create(CallKwRequest::class.java)
- val requestBody = CallKwReqBody(id = jsonRpcId, params = CallKwParams(
+ val requestBody = CallKwReqBody(
+ id = jsonRpcId, params = CallKwParams(
model, method, args, kwArgs, context
- ))
+ )
+ )
val observable = request.callKw(requestBody)
observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(ResponseObserver().apply(callback))
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(ResponseObserver().apply(callback))
}
fun execWorkflow(
- model: String,
- id: Int,
- signal: String,
- context: JsonObject = user.context,
- callback: ResponseObserver.() -> Unit
+ model: String,
+ id: Int,
+ signal: String,
+ context: JsonObject = user.context,
+ callback: ResponseObserver.() -> Unit
) {
val request = retrofit.create(ExecWorkflowRequest::class.java)
- val requestBody = ExecWorkflowReqBody(id = jsonRpcId, params = ExecWorkflowParams(
+ val requestBody = ExecWorkflowReqBody(
+ id = jsonRpcId, params = ExecWorkflowParams(
model, id, signal, context
- ))
+ )
+ )
val observable = request.execWorkflow(requestBody)
observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(ResponseObserver().apply(callback))
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(ResponseObserver().apply(callback))
}
fun route(
- path1: String,
- path2: String,
- args: Map,
- callback: ResponseObserver.() -> Unit
+ path1: String,
+ path2: String,
+ args: Any = mapOf(),
+ callback: ResponseObserver.() -> Unit
) {
val request = retrofit.create(RouteRequest::class.java)
val requestBody = RouteReqBody(id = jsonRpcId, params = args)
val observable = request.route(path1, path2, requestBody)
observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(ResponseObserver().apply(callback))
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(ResponseObserver().apply(callback))
}
fun route3Path(
- path1: String,
- path2: String,
- path3: String,
- args: Map,
- callback: ResponseObserver.() -> Unit
+ path1: String,
+ path2: String,
+ path3: String,
+ args: Any = mapOf(),
+ callback: ResponseObserver.() -> Unit
) {
val request = retrofit.create(Route3PathRequest::class.java)
val requestBody = RouteReqBody(id = jsonRpcId, params = args)
val observable = request.route(path1, path2, path3, requestBody)
observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(ResponseObserver().apply(callback))
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(ResponseObserver().apply(callback))
}
fun route4Path(
- path1: String,
- path2: String,
- path3: String,
- path4: String,
- args: Map,
- callback: ResponseObserver.() -> Unit
+ path1: String,
+ path2: String,
+ path3: String,
+ path4: String,
+ args: Any = mapOf(),
+ callback: ResponseObserver.() -> Unit
) {
val request = retrofit.create(Route4PathRequest::class.java)
val requestBody = RouteReqBody(id = jsonRpcId, params = args)
val observable = request.route(path1, path2, path3, path4, requestBody)
observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(ResponseObserver().apply(callback))
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(ResponseObserver().apply(callback))
}
fun create(
- model: String,
- values: Map,
- kwArgs: Map = mapOf(),
- context: JsonObject = user.context,
- callback: ResponseObserver.() -> Unit
+ model: String,
+ values: Map,
+ kwArgs: Map = mapOf(),
+ context: JsonObject = user.context,
+ callback: ResponseObserver.() -> Unit
) {
val callbackEx = ResponseObserver()
callbackEx.callback()
@@ -354,15 +367,19 @@ object Odoo {
onNext { response ->
callbackEx.onNext(
- if (response.isSuccessful)
- Response.success(Create(
- if (response.body()!!.isSuccessful)
- response.body()!!.result.asLong
- else
- 0L
- , response.body()!!.odooError))
- else
- Response.error(response.code(), response.errorBody()!!))
+ if (response.isSuccessful)
+ Response.success(
+ Create(
+ if (response.body()!!.isSuccessful)
+ response.body()!!.result.asLong
+ else
+ 0L
+ , response.body()!!.odooError
+ )
+ )
+ else
+ Response.error(response.code(), response.errorBody()!!)
+ )
}
onError { error ->
@@ -376,12 +393,12 @@ object Odoo {
}
fun read(
- model: String,
- ids: List,
- fields: List,
- kwArgs: Map = mapOf(),
- context: JsonObject = user.context,
- callback: ResponseObserver.() -> Unit
+ model: String,
+ ids: List,
+ fields: List,
+ kwArgs: Map = mapOf(),
+ context: JsonObject = user.context,
+ callback: ResponseObserver.() -> Unit
) {
val callbackEx = ResponseObserver()
callbackEx.callback()
@@ -392,15 +409,19 @@ object Odoo {
onNext { response ->
callbackEx.onNext(
- if (response.isSuccessful)
- Response.success(Read(
- if (response.body()!!.isSuccessful)
- response.body()!!.result
- else
- JsonArray()
- , response.body()!!.odooError))
- else
- Response.error(response.code(), response.errorBody()!!))
+ if (response.isSuccessful)
+ Response.success(
+ Read(
+ if (response.body()!!.isSuccessful)
+ response.body()!!.result
+ else
+ JsonArray()
+ , response.body()!!.odooError
+ )
+ )
+ else
+ Response.error(response.code(), response.errorBody()!!)
+ )
}
onError { error ->
@@ -414,12 +435,12 @@ object Odoo {
}
fun write(
- model: String,
- ids: List,
- values: Map,
- kwArgs: Map = mapOf(),
- context: JsonObject = user.context,
- callback: ResponseObserver.() -> Unit
+ model: String,
+ ids: List,
+ values: Map,
+ kwArgs: Map = mapOf(),
+ context: JsonObject = user.context,
+ callback: ResponseObserver.() -> Unit
) {
val callbackEx = ResponseObserver()
callbackEx.callback()
@@ -430,15 +451,19 @@ object Odoo {
onNext { response ->
callbackEx.onNext(
- if (response.isSuccessful)
- Response.success(Write(
- if (response.body()!!.isSuccessful)
- response.body()!!.result.asBoolean
- else
- false
- , response.body()!!.odooError))
- else
- Response.error(response.code(), response.errorBody()!!))
+ if (response.isSuccessful)
+ Response.success(
+ Write(
+ if (response.body()!!.isSuccessful)
+ response.body()!!.result.asBoolean
+ else
+ false
+ , response.body()!!.odooError
+ )
+ )
+ else
+ Response.error(response.code(), response.errorBody()!!)
+ )
}
onError { error ->
@@ -452,11 +477,11 @@ object Odoo {
}
fun unlink(
- model: String,
- ids: List,
- kwArgs: Map = mapOf(),
- context: JsonObject = user.context,
- callback: ResponseObserver.() -> Unit
+ model: String,
+ ids: List,
+ kwArgs: Map = mapOf(),
+ context: JsonObject = user.context,
+ callback: ResponseObserver.() -> Unit
) {
val callbackEx = ResponseObserver()
callbackEx.callback()
@@ -467,15 +492,19 @@ object Odoo {
onNext { response ->
callbackEx.onNext(
- if (response.isSuccessful)
- Response.success(Unlink(
- if (response.body()!!.isSuccessful)
- response.body()!!.result.asBoolean
- else
- false
- , response.body()!!.odooError))
- else
- Response.error(response.code(), response.errorBody()!!))
+ if (response.isSuccessful)
+ Response.success(
+ Unlink(
+ if (response.body()!!.isSuccessful)
+ response.body()!!.result.asBoolean
+ else
+ false
+ , response.body()!!.odooError
+ )
+ )
+ else
+ Response.error(response.code(), response.errorBody()!!)
+ )
}
onError { error ->
@@ -489,11 +518,11 @@ object Odoo {
}
fun nameGet(
- model: String,
- ids: List,
- kwArgs: Map = mapOf(),
- context: JsonObject = user.context,
- callback: ResponseObserver.() -> Unit
+ model: String,
+ ids: List,
+ kwArgs: Map = mapOf(),
+ context: JsonObject = user.context,
+ callback: ResponseObserver.() -> Unit
) {
val callbackEx = ResponseObserver()
callbackEx.callback()
@@ -504,15 +533,19 @@ object Odoo {
onNext { response ->
callbackEx.onNext(
- if (response.isSuccessful)
- Response.success(NameGet(
- if (response.body()!!.isSuccessful)
- response.body()!!.result.asJsonArray
- else
- JsonArray()
- , response.body()!!.odooError))
- else
- Response.error(response.code(), response.errorBody()!!))
+ if (response.isSuccessful)
+ Response.success(
+ NameGet(
+ if (response.body()!!.isSuccessful)
+ response.body()!!.result.asJsonArray
+ else
+ JsonArray()
+ , response.body()!!.odooError
+ )
+ )
+ else
+ Response.error(response.code(), response.errorBody()!!)
+ )
}
onError { error ->
@@ -526,11 +559,11 @@ object Odoo {
}
fun nameCreate(
- model: String,
- name: String,
- kwArgs: Map = mapOf(),
- context: JsonObject = user.context,
- callback: ResponseObserver.() -> Unit
+ model: String,
+ name: String,
+ kwArgs: Map = mapOf(),
+ context: JsonObject = user.context,
+ callback: ResponseObserver.() -> Unit
) {
val callbackEx = ResponseObserver()
callbackEx.callback()
@@ -541,15 +574,19 @@ object Odoo {
onNext { response ->
callbackEx.onNext(
- if (response.isSuccessful)
- Response.success(NameCreate(
- if (response.body()!!.isSuccessful)
- response.body()!!.result.asJsonArray
- else
- JsonArray()
- , response.body()!!.odooError))
- else
- Response.error(response.code(), response.errorBody()!!))
+ if (response.isSuccessful)
+ Response.success(
+ NameCreate(
+ if (response.body()!!.isSuccessful)
+ response.body()!!.result.asJsonArray
+ else
+ JsonArray()
+ , response.body()!!.odooError
+ )
+ )
+ else
+ Response.error(response.code(), response.errorBody()!!)
+ )
}
onError { error ->
@@ -563,37 +600,43 @@ object Odoo {
}
fun nameSearch(
- model: String,
- name: String = "",
- args: List = listOf(),
- operator: String = "ilike",
- limit: Int = 0,
- context: JsonObject = user.context,
- callback: ResponseObserver.() -> Unit
+ model: String,
+ name: String = "",
+ args: List = listOf(),
+ operator: String = "ilike",
+ limit: Int = 0,
+ context: JsonObject = user.context,
+ callback: ResponseObserver.() -> Unit
) {
val callbackEx = ResponseObserver()
callbackEx.callback()
- callKw(model, "name_search", listOf(), mapOf(
+ callKw(
+ model, "name_search", listOf(), mapOf(
"name" to name,
"args" to args,
"operator" to operator,
"limit" to limit
- ), context) {
+ ), context
+ ) {
onSubscribe { disposable ->
callbackEx.onSubscribe(disposable)
}
onNext { response ->
callbackEx.onNext(
- if (response.isSuccessful)
- Response.success(NameSearch(
- if (response.body()!!.isSuccessful)
- response.body()!!.result.asJsonArray
- else
- JsonArray()
- , response.body()!!.odooError))
- else
- Response.error(response.code(), response.errorBody()!!))
+ if (response.isSuccessful)
+ Response.success(
+ NameSearch(
+ if (response.body()!!.isSuccessful)
+ response.body()!!.result.asJsonArray
+ else
+ JsonArray()
+ , response.body()!!.odooError
+ )
+ )
+ else
+ Response.error(response.code(), response.errorBody()!!)
+ )
}
onError { error ->
@@ -607,15 +650,15 @@ object Odoo {
}
fun search(
- model: String,
- domain: List = listOf(),
- offset: Int = 0,
- limit: Int = 0,
- sort: String = "",
- count: Boolean = false,
- kwArgs: Map = mapOf(),
- context: JsonObject = user.context,
- callback: ResponseObserver.() -> Unit
+ model: String,
+ domain: List = listOf(),
+ offset: Int = 0,
+ limit: Int = 0,
+ sort: String = "",
+ count: Boolean = false,
+ kwArgs: Map = mapOf(),
+ context: JsonObject = user.context,
+ callback: ResponseObserver.() -> Unit
) {
val callbackEx = ResponseObserver()
callbackEx.callback()
@@ -626,15 +669,19 @@ object Odoo {
onNext { response ->
callbackEx.onNext(
- if (response.isSuccessful)
- Response.success(Search(
- if (response.body()!!.isSuccessful)
- response.body()!!.result.asJsonArray.asIntList
- else
- listOf()
- , response.body()!!.odooError))
- else
- Response.error(response.code(), response.errorBody()!!))
+ if (response.isSuccessful)
+ Response.success(
+ Search(
+ if (response.body()!!.isSuccessful)
+ response.body()!!.result.asJsonArray.asIntList
+ else
+ listOf()
+ , response.body()!!.odooError
+ )
+ )
+ else
+ Response.error(response.code(), response.errorBody()!!)
+ )
}
onError { error ->
@@ -648,11 +695,11 @@ object Odoo {
}
fun searchCount(
- model: String,
- args: List = listOf(),
- kwArgs: Map = mapOf(),
- context: JsonObject = user.context,
- callback: ResponseObserver.() -> Unit
+ model: String,
+ args: List = listOf(),
+ kwArgs: Map = mapOf(),
+ context: JsonObject = user.context,
+ callback: ResponseObserver.() -> Unit
) {
val callbackEx = ResponseObserver()
callbackEx.callback()
@@ -663,15 +710,19 @@ object Odoo {
onNext { response ->
callbackEx.onNext(
- if (response.isSuccessful)
- Response.success(SearchCount(
- if (response.body()!!.isSuccessful)
- response.body()!!.result.asInt
- else
- 0
- , response.body()!!.odooError))
- else
- Response.error(response.code(), response.errorBody()!!))
+ if (response.isSuccessful)
+ Response.success(
+ SearchCount(
+ if (response.body()!!.isSuccessful)
+ response.body()!!.result.asInt
+ else
+ 0
+ , response.body()!!.odooError
+ )
+ )
+ else
+ Response.error(response.code(), response.errorBody()!!)
+ )
}
onError { error ->
@@ -685,12 +736,12 @@ object Odoo {
}
fun checkAccessRights(
- model: String,
- operation: String,
- raiseException: Boolean = false,
- kwArgs: Map = mapOf(),
- context: JsonObject = user.context,
- callback: ResponseObserver.() -> Unit
+ model: String,
+ operation: String,
+ raiseException: Boolean = false,
+ kwArgs: Map = mapOf(),
+ context: JsonObject = user.context,
+ callback: ResponseObserver.() -> Unit
) {
val callbackEx = ResponseObserver()
callbackEx.callback()
@@ -701,15 +752,19 @@ object Odoo {
onNext { response ->
callbackEx.onNext(
- if (response.isSuccessful)
- Response.success(CheckAccessRights(
- if (response.body()!!.isSuccessful)
- response.body()!!.result.asBoolean
- else
- false
- , response.body()!!.odooError))
- else
- Response.error(response.code(), response.errorBody()!!))
+ if (response.isSuccessful)
+ Response.success(
+ CheckAccessRights(
+ if (response.body()!!.isSuccessful)
+ response.body()!!.result.asBoolean
+ else
+ false
+ , response.body()!!.odooError
+ )
+ )
+ else
+ Response.error(response.code(), response.errorBody()!!)
+ )
}
onError { error ->
@@ -723,33 +778,36 @@ object Odoo {
}
fun fieldsGet(
- model: String = "",
- fields: List = listOf(),
- callback: ResponseObserver.() -> Unit
+ model: String = "",
+ fields: List = listOf(),
+ callback: ResponseObserver.() -> Unit
) =
- searchRead("ir.model.fields", fields,
- if (model.isNotEmpty()) listOf(listOf("model_id", "=", model)) else listOf(),
- callback = callback
- )
+ searchRead(
+ "ir.model.fields", fields,
+ if (model.isNotEmpty()) listOf(listOf("model_id", "=", model)) else listOf(),
+ callback = callback
+ )
fun accessGet(
- model: String = "",
- fields: List = listOf(),
- callback: ResponseObserver.() -> Unit
+ model: String = "",
+ fields: List = listOf(),
+ callback: ResponseObserver.() -> Unit
) =
- searchRead("ir.model.access", fields,
- if (model.isNotEmpty()) listOf(listOf("model_id", "=", model)) else listOf(),
- callback = callback
- )
+ searchRead(
+ "ir.model.access", fields,
+ if (model.isNotEmpty()) listOf(listOf("model_id", "=", model)) else listOf(),
+ callback = callback
+ )
fun groupsGet(
- fields: List = listOf(),
- callback: ResponseObserver.() -> Unit
+ fields: List = listOf(),
+ callback: ResponseObserver.() -> Unit
) =
- searchRead("res.groups", fields,
- listOf(listOf("users", "in", listOf(user.id))),
- callback = callback
- )
+ searchRead(
+ "res.groups", fields,
+ listOf(listOf("users", "in", listOf(user.id))),
+ callback = callback
+ )
}
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/OdooUser.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/OdooUser.kt
index 63b577b..3c58b4e 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/OdooUser.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/OdooUser.kt
@@ -1,29 +1,30 @@
package io.gripxtech.odoojsonrpcclient.core
import android.accounts.Account
-import android.databinding.BindingAdapter
import android.util.Base64
import android.widget.ImageView
+import androidx.databinding.BindingAdapter
import com.google.gson.JsonObject
import io.gripxtech.odoojsonrpcclient.App
import io.gripxtech.odoojsonrpcclient.GlideApp
import io.gripxtech.odoojsonrpcclient.core.utils.Retrofit2Helper
data class OdooUser(
- val protocol: Retrofit2Helper.Companion.Protocol = Retrofit2Helper.Companion.Protocol.HTTP,
- val host: String = "",
- val login: String = "",
- val password: String = "",
- val database: String = "",
- val serverVersion: String = "",
- val isAdmin: Boolean = false,
- val id: Int = 0,
- val name: String = "",
- val imageSmall: String = "",
- val partnerId: Int = 0,
- val context: JsonObject = JsonObject(),
- val isActive: Boolean = false,
- val account: Account = Account("false", App.KEY_ACCOUNT_TYPE)
+ val protocol: Retrofit2Helper.Companion.Protocol = Retrofit2Helper.Companion.Protocol.HTTP,
+ val host: String = "",
+ val login: String = "",
+ val password: String = "",
+ val database: String = "",
+ val serverVersion: String = "",
+ val isAdmin: Boolean = false,
+ val isSuperUser: Boolean = false,
+ val id: Int = 0,
+ val name: String = "",
+ val imageSmall: String = "",
+ val partnerId: Int = 0,
+ val context: JsonObject = JsonObject(),
+ val isActive: Boolean = false,
+ val account: Account = Account("false", App.KEY_ACCOUNT_TYPE)
) {
val androidName: String
get() = "$login[$database]"
@@ -36,14 +37,16 @@ data class OdooUser(
@BindingAdapter("image_small", "name")
fun loadImage(view: ImageView, imageSmall: String, name: String) {
GlideApp.with(view.context)
- .asBitmap()
- .load(
- if (imageSmall.isNotEmpty())
- Base64.decode(imageSmall, Base64.DEFAULT)
- else
- (view.context.applicationContext as App)
- .getLetterTile(if (name.isNotEmpty()) name else "X"))
- .into(view)
+ .asBitmap()
+ .load(
+ if (imageSmall.isNotEmpty())
+ Base64.decode(imageSmall, Base64.DEFAULT)
+ else
+ (view.context.applicationContext as App)
+ .getLetterTile(if (name.isNotEmpty()) name else "X")
+ )
+ .circleCrop()
+ .into(view)
}
}
}
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/AccountAuthenticator.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/AccountAuthenticator.kt
index 3d40a09..6d13b9c 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/AccountAuthenticator.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/AccountAuthenticator.kt
@@ -21,8 +21,9 @@ class AccountAuthenticator(
requiredFeatures: Array?,
options: Bundle?
): Bundle {
- val intent = Intent(context, LoginActivity::class.java)
- intent.putExtra(LoginActivity.FROM_ANDROID_ACCOUNTS, true)
+// val intent = Intent(context, LoginActivity::class.java)
+// intent.putExtra(LoginActivity.FROM_ANDROID_ACCOUNTS, true)
+ val intent = Intent(context, SplashActivity::class.java)
val bundle = Bundle()
bundle.putParcelable(AccountManager.KEY_INTENT, intent)
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/LoginActivity.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/LoginActivity.kt
index 7a20288..32c9543 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/LoginActivity.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/LoginActivity.kt
@@ -1,17 +1,20 @@
package io.gripxtech.odoojsonrpcclient.core.authenticator
import android.app.Activity
+import android.content.Context
import android.content.Intent
-import android.databinding.DataBindingUtil
+import android.content.res.Configuration
import android.os.Bundle
-import android.support.v7.app.AppCompatActivity
-import android.support.v7.app.AppCompatDelegate
import android.view.View
import android.widget.ArrayAdapter
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.app.AppCompatDelegate
+import androidx.databinding.DataBindingUtil
import io.gripxtech.odoojsonrpcclient.*
import io.gripxtech.odoojsonrpcclient.core.Odoo
import io.gripxtech.odoojsonrpcclient.core.entities.session.authenticate.AuthenticateResult
import io.gripxtech.odoojsonrpcclient.core.entities.webclient.versionInfo.VersionInfo
+import io.gripxtech.odoojsonrpcclient.core.utils.LocaleHelper
import io.gripxtech.odoojsonrpcclient.core.utils.Retrofit2Helper
import io.gripxtech.odoojsonrpcclient.core.utils.android.ktx.addTextChangedListenerEx
import io.gripxtech.odoojsonrpcclient.core.utils.android.ktx.postEx
@@ -39,22 +42,42 @@ class LoginActivity : AppCompatActivity() {
private lateinit var app: App
private lateinit var binding: ActivityLoginBinding
- private lateinit var compositeDisposable: CompositeDisposable
+ private var compositeDisposable: CompositeDisposable? = null
private var selfHostedUrl: Boolean = false
+ private var preConfigDatabase: Boolean = false
+ private var preConfigDatabaseName: String = ""
+
+ override fun attachBaseContext(newBase: Context?) {
+ if (newBase != null) {
+ super.attachBaseContext(LocaleHelper.setLocale(newBase))
+ } else {
+ super.attachBaseContext(newBase)
+ }
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ LocaleHelper.setLocale(this)
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
app = application as App
binding = DataBindingUtil.setContentView(this, R.layout.activity_login)
+ compositeDisposable?.dispose()
compositeDisposable = CompositeDisposable()
selfHostedUrl = resources.getBoolean(R.bool.self_hosted_url)
+ preConfigDatabase = resources.getBoolean(R.bool.pre_config_database)
+ preConfigDatabaseName = getString(R.string.pre_config_database_name)
}
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
if (selfHostedUrl) {
- binding.grpCheckVersion.visibility = View.VISIBLE
+ binding.grpCheckVersion.postEx {
+ visibility = View.VISIBLE
+ }
binding.spProtocol.setOnItemSelectedListenerEx {
onItemSelected { _, _, _, _ ->
resetLoginLayout(resetCheckVersion = true)
@@ -64,28 +87,52 @@ class LoginActivity : AppCompatActivity() {
binding.tlHost.isErrorEnabled = false
binding.etHost.addTextChangedListenerEx {
- afterTextChanged { _ ->
+ afterTextChanged {
binding.tlHost.isErrorEnabled = false
resetLoginLayout(resetCheckVersion = true)
}
}
binding.bnCheckVersion.setOnClickListener {
- if (binding.etHost.text.toString().isBlank()) {
+ val host = binding.etHost.text.toString().trim()
+ if (host.isBlank()) {
binding.tlHost.error = getString(R.string.login_host_error)
return@setOnClickListener
}
+ if (host.startsWith("http")) {
+ binding.tlHost.error = getString(R.string.login_host_error1)
+ return@setOnClickListener
+ }
+
hideSoftKeyboard()
binding.spProtocol.isEnabled = false
binding.tlHost.isEnabled = false
binding.bnCheckVersion.isEnabled = false
resetLoginLayout(resetCheckVersion = false)
prepareUiForCheckVersion()
+ Odoo.protocol = when (binding.spProtocol.selectedItemPosition) {
+ 0 -> {
+ Retrofit2Helper.Companion.Protocol.HTTP
+ }
+ else -> {
+ Retrofit2Helper.Companion.Protocol.HTTPS
+ }
+ }
+ Odoo.host = binding.etHost.text.toString()
checkVersion()
}
} else {
prepareUiForCheckVersion()
+ Odoo.protocol = when (resources.getInteger(R.integer.protocol)) {
+ 0 -> {
+ Retrofit2Helper.Companion.Protocol.HTTP
+ }
+ else -> {
+ Retrofit2Helper.Companion.Protocol.HTTPS
+ }
+ }
+ Odoo.host = getString(R.string.host_url)
checkVersion()
}
@@ -103,7 +150,7 @@ class LoginActivity : AppCompatActivity() {
}
val database = binding.spDatabase.selectedItem
- if (database != null && database.toString().isBlank()) {
+ if (database == null || database.toString().isBlank()) {
showMessage(message = getString(R.string.login_database_error))
return@setOnClickListener
}
@@ -115,7 +162,9 @@ class LoginActivity : AppCompatActivity() {
val users = getOdooUsers()
if (users.isNotEmpty()) {
- binding.bnOtherAccount.visibility = View.VISIBLE
+ binding.bnOtherAccount.postEx {
+ visibility = View.VISIBLE
+ }
binding.bnOtherAccount.setOnClickListener {
startActivity(Intent(this@LoginActivity, ManageAccountActivity::class.java))
}
@@ -123,25 +172,24 @@ class LoginActivity : AppCompatActivity() {
}
private fun prepareUiForCheckVersion() {
- binding.llCheckingVersion.visibility = View.VISIBLE
- binding.llCheckVersionResult.visibility = View.GONE
- binding.ivCheckVersionResultSuccess.visibility = View.GONE
- binding.ivCheckVersionResultFail.visibility = View.GONE
- Odoo.protocol = when (resources.getInteger(R.integer.protocol)) {
- 0 -> {
- Retrofit2Helper.Companion.Protocol.HTTP
- }
- else -> {
- Retrofit2Helper.Companion.Protocol.HTTPS
- }
+ binding.llCheckingVersion.postEx {
+ visibility = View.VISIBLE
+ }
+ binding.llCheckVersionResult.postEx {
+ visibility = View.GONE
+ }
+ binding.ivCheckVersionResultSuccess.postEx {
+ visibility = View.GONE
+ }
+ binding.ivCheckVersionResultFail.postEx {
+ visibility = View.GONE
}
- Odoo.host = getString(R.string.host_url)
}
private fun checkVersion() {
Odoo.versionInfo {
onSubscribe { disposable ->
- compositeDisposable.add(disposable)
+ compositeDisposable?.add(disposable)
}
onNext { response ->
@@ -151,22 +199,29 @@ class LoginActivity : AppCompatActivity() {
if (versionInfo.result.serverVersionIsSupported) {
getDbList(versionInfo)
} else {
- toggleCheckVersionWidgets(isSuccess = false, resultMessage = getString(
+ toggleCheckVersionWidgets(
+ isSuccess = false, resultMessage = getString(
R.string.login_server_error,
versionInfo.result.serverVersion
- ))
+ )
+ )
}
} else {
toggleCheckVersionWidgets(isSuccess = false, resultMessage = versionInfo.errorMessage)
}
} else {
- toggleCheckVersionWidgets(isSuccess = false, resultMessage = "${response.code()}: ${response.message()}")
+ toggleCheckVersionWidgets(
+ isSuccess = false,
+ resultMessage = "${response.code()}: ${response.message()}"
+ )
}
}
onError { error ->
- toggleCheckVersionWidgets(isSuccess = false, resultMessage = error.message
- ?: getString(R.string.generic_error))
+ toggleCheckVersionWidgets(
+ isSuccess = false, resultMessage = error.message
+ ?: getString(R.string.generic_error)
+ )
}
}
}
@@ -174,36 +229,52 @@ class LoginActivity : AppCompatActivity() {
private fun getDbList(versionInfo: VersionInfo) {
Odoo.listDb(versionInfo.result.serverVersion) {
onSubscribe { disposable ->
- compositeDisposable.add(disposable)
+ compositeDisposable?.add(disposable)
}
onNext { response ->
if (response.isSuccessful) {
val listDb = response.body()!!
if (listDb.isSuccessful) {
- toggleCheckVersionWidgets(isSuccess = true, resultMessage = getString(
+ toggleCheckVersionWidgets(
+ isSuccess = true, resultMessage = getString(
R.string.login_server_success,
versionInfo.result.serverVersion
- ))
-
- binding.spDatabase.adapter = ArrayAdapter(
+ )
+ )
+ if (preConfigDatabase && listDb.result.contains(preConfigDatabaseName)) {
+ binding.spDatabase.adapter = ArrayAdapter(
+ this@LoginActivity,
+ R.layout.support_simple_spinner_dropdown_item,
+ listOf(preConfigDatabaseName)
+ )
+ changeGroupLoginVisibility(View.VISIBLE)
+ changeDbSpinnerVisibility(View.GONE)
+ } else {
+ binding.spDatabase.adapter = ArrayAdapter(
this@LoginActivity,
R.layout.support_simple_spinner_dropdown_item,
listDb.result
- )
- binding.groupLogin.visibility = View.VISIBLE
- changeDbSpinnerVisibility(if (listDb.result.size == 1) View.GONE else View.VISIBLE)
+ )
+ changeGroupLoginVisibility(View.VISIBLE)
+ changeDbSpinnerVisibility(if (listDb.result.size == 1) View.GONE else View.VISIBLE)
+ }
} else {
toggleCheckVersionWidgets(isSuccess = false, resultMessage = listDb.errorMessage)
}
} else {
- toggleCheckVersionWidgets(isSuccess = false, resultMessage = "${response.code()}: ${response.message()}")
+ toggleCheckVersionWidgets(
+ isSuccess = false,
+ resultMessage = "${response.code()}: ${response.message()}"
+ )
}
}
onError { error ->
- toggleCheckVersionWidgets(isSuccess = false, resultMessage = error.message
- ?: getString(R.string.generic_error))
+ toggleCheckVersionWidgets(
+ isSuccess = false, resultMessage = error.message
+ ?: getString(R.string.generic_error)
+ )
}
}
}
@@ -212,25 +283,45 @@ class LoginActivity : AppCompatActivity() {
binding.spProtocol.isEnabled = true
binding.tlHost.isEnabled = true
binding.bnCheckVersion.isEnabled = true
- binding.llCheckingVersion.visibility = View.GONE
- binding.llCheckVersionResult.visibility = View.VISIBLE
+ binding.llCheckingVersion.postEx {
+ visibility = View.GONE
+ }
+ binding.llCheckVersionResult.postEx {
+ visibility = View.VISIBLE
+ }
if (isSuccess) {
- binding.ivCheckVersionResultSuccess.visibility = View.VISIBLE
- binding.ivCheckVersionResultFail.visibility = View.GONE
+ binding.ivCheckVersionResultSuccess.postEx {
+ visibility = View.VISIBLE
+ }
+ binding.ivCheckVersionResultFail.postEx {
+ visibility = View.GONE
+ }
} else {
- binding.ivCheckVersionResultSuccess.visibility = View.GONE
- binding.ivCheckVersionResultFail.visibility = View.VISIBLE
+ binding.ivCheckVersionResultSuccess.postEx {
+ visibility = View.GONE
+ }
+ binding.ivCheckVersionResultFail.postEx {
+ visibility = View.VISIBLE
+ }
}
binding.tvCheckVersionResultMessage.text = resultMessage
}
private fun resetLoginLayout(resetCheckVersion: Boolean = false) {
if (resetCheckVersion) {
- binding.llCheckingVersion.visibility = View.GONE
- binding.llCheckVersionResult.visibility = View.GONE
- binding.ivCheckVersionResultSuccess.visibility = View.GONE
- binding.ivCheckVersionResultFail.visibility = View.GONE
+ binding.llCheckingVersion.postEx {
+ visibility = View.GONE
+ }
+ binding.llCheckVersionResult.postEx {
+ visibility = View.GONE
+ }
+ binding.ivCheckVersionResultSuccess.postEx {
+ visibility = View.GONE
+ }
+ binding.ivCheckVersionResultFail.postEx {
+ visibility = View.GONE
+ }
}
// changeDbSpinnerVisibility(View.GONE)
binding.spDatabase.adapter.run {
@@ -240,22 +331,78 @@ class LoginActivity : AppCompatActivity() {
}
}
}
- binding.groupLogin.visibility = View.GONE
- binding.llLoginProgress.visibility = View.GONE
- binding.llLoginError.visibility = View.GONE
+ binding.spcLoginTop.postEx {
+ visibility = View.GONE
+ }
+ binding.tlLogin.postEx {
+ visibility = View.GONE
+ }
+ binding.tlPassword.postEx {
+ visibility = View.GONE
+ }
+ binding.lblDatabase.postEx {
+ visibility = View.GONE
+ }
+ binding.spcDatabaseTop.postEx {
+ visibility = View.GONE
+ }
+ binding.spDatabase.postEx {
+ visibility = View.GONE
+ }
+ binding.spcDatabaseBottom.postEx {
+ visibility = View.GONE
+ }
+ binding.bn.postEx {
+ visibility = View.GONE
+ }
+ binding.llLoginProgress.postEx {
+ visibility = View.GONE
+ }
+ binding.llLoginError.postEx {
+ visibility = View.GONE
+ }
}
/**
* Set the visibility state of this view.
*
- * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
+ * @param flag One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
* @attr ref android.R.styleable#View_visibility
*/
- private fun changeDbSpinnerVisibility(visibility: Int) {
- binding.lblDatabase.visibility = visibility
- binding.spcDatabaseTop.visibility = visibility
- binding.spDatabase.visibility = visibility
- binding.spcDatabaseBottom.visibility = visibility
+ private fun changeGroupLoginVisibility(flag: Int) {
+ binding.spcLoginTop.postEx {
+ visibility = flag
+ }
+ binding.tlLogin.postEx {
+ visibility = flag
+ }
+ binding.tlPassword.postEx {
+ visibility = flag
+ }
+ binding.bn.postEx {
+ visibility = flag
+ }
+ }
+
+ /**
+ * Set the visibility state of this view.
+ *
+ * @param flag One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
+ * @attr ref android.R.styleable#View_visibility
+ */
+ private fun changeDbSpinnerVisibility(flag: Int) {
+ binding.lblDatabase.postEx {
+ visibility = flag
+ }
+ binding.spcDatabaseTop.postEx {
+ visibility = flag
+ }
+ binding.spDatabase.postEx {
+ visibility = flag
+ }
+ binding.spcDatabaseBottom.postEx {
+ visibility = flag
+ }
}
private fun prepareUiForAuthenticate() {
@@ -269,14 +416,18 @@ class LoginActivity : AppCompatActivity() {
binding.spDatabase.isEnabled = false
binding.bn.isEnabled = false
- binding.llLoginProgress.visibility = View.VISIBLE
- binding.llLoginError.visibility = View.GONE
+ binding.llLoginProgress.postEx {
+ visibility = View.VISIBLE
+ }
+ binding.llLoginError.postEx {
+ visibility = View.GONE
+ }
}
private fun authenticate(login: String, password: String, database: String) {
Odoo.authenticate(login = login, password = password, database = database) {
onSubscribe { disposable ->
- compositeDisposable.add(disposable)
+ compositeDisposable?.add(disposable)
}
onNext { response ->
@@ -287,7 +438,19 @@ class LoginActivity : AppCompatActivity() {
authenticateResult.password = password
searchReadUserInfo(authenticateResult = authenticateResult)
} else {
- toggleLoginWidgets(showErrorBody = true, errorMessage = authenticate.errorMessage)
+ val errorMessage = authenticate.errorMessage
+ toggleLoginWidgets(
+ showErrorBody = true,
+ errorMessage = if (errorMessage.contains(
+ "Expected singleton: res.users()",
+ ignoreCase = true
+ )
+ ) {
+ getString(R.string.login_credential_error)
+ } else {
+ authenticate.errorMessage
+ }
+ )
}
} else {
toggleLoginWidgets(showErrorBody = false)
@@ -296,30 +459,43 @@ class LoginActivity : AppCompatActivity() {
}
onError { error ->
- toggleLoginWidgets(showErrorBody = true, errorMessage = error.message
- ?: getString(R.string.generic_error))
+ val pattern1 = "an int but was BOOLEAN"
+ val pattern2 = "result.uid"
+ val message = error.message ?: getString(R.string.generic_error)
+ toggleLoginWidgets(
+ showErrorBody = true, errorMessage = if (message.contains(pattern1) && message.contains(pattern2))
+ getString(R.string.login_credential_error)
+ else
+ message
+ )
}
}
}
private fun searchReadUserInfo(authenticateResult: AuthenticateResult) {
Odoo.searchRead(
- model = "res.users",
- fields = listOf("name", "image"),
- domain = listOf(listOf("id", "=", authenticateResult.uid)),
- offset = 0, limit = 0, sort = "id DESC", context = authenticateResult.userContext
+ model = "res.users",
+ fields = listOf("name", "image"),
+ domain = listOf(listOf("id", "=", authenticateResult.uid)),
+ offset = 0, limit = 0, sort = "id DESC", context = authenticateResult.userContext
) {
onSubscribe { disposable ->
- compositeDisposable.add(disposable)
+ compositeDisposable?.add(disposable)
}
onNext { response ->
if (response.isSuccessful) {
val searchRead = response.body()!!
if (searchRead.isSuccessful) {
- val row = searchRead.result.records[0].asJsonObject
- authenticateResult.imageSmall = row.get("image").asString
- authenticateResult.name = row.get("name").asString.trimFalse()
+ if (searchRead.result.records.size() > 0) {
+ val row = searchRead.result.records[0].asJsonObject
+ row?.get("image")?.asString?.let {
+ authenticateResult.imageSmall = it
+ }
+ row?.get("name")?.asString?.trimFalse()?.let {
+ authenticateResult.name = it
+ }
+ }
binding.tvLoginProgress.text = getString(R.string.login_success)
createAccount(authenticateResult = authenticateResult)
} else {
@@ -332,8 +508,10 @@ class LoginActivity : AppCompatActivity() {
}
onError { error ->
- toggleLoginWidgets(showErrorBody = true, errorMessage = error.message
- ?: getString(R.string.generic_error))
+ toggleLoginWidgets(
+ showErrorBody = true, errorMessage = error.message
+ ?: getString(R.string.generic_error)
+ )
}
}
}
@@ -349,10 +527,14 @@ class LoginActivity : AppCompatActivity() {
binding.spDatabase.isEnabled = true
binding.bn.isEnabled = true
- binding.llLoginProgress.visibility = View.GONE
+ binding.llLoginProgress.postEx {
+ visibility = View.GONE
+ }
if (showErrorBody) {
- binding.llLoginError.visibility = View.VISIBLE
+ binding.llLoginError.postEx {
+ visibility = View.VISIBLE
+ }
binding.tvLoginError.text = errorMessage
}
}
@@ -372,33 +554,33 @@ class LoginActivity : AppCompatActivity() {
false
}
}
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribeEx {
- onSubscribe { _ ->
- // Must be complete, not dispose in between
- // compositeDisposable.add(d)
- }
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribeEx {
+ onSubscribe {
+ // Must be complete, not dispose in between
+ // compositeDisposable.add(d)
+ }
- onNext { t ->
- resultCallback(t)
- }
+ onNext { t ->
+ resultCallback(t)
+ }
- onError { error ->
- error.printStackTrace()
- if (!isFinishing && !isDestroyed) {
- binding.llLoginProgress.postEx {
- visibility = View.GONE
- }
- binding.llLoginError.postEx {
- visibility = View.VISIBLE
- }
- binding.tvLoginError.postEx {
- text = getString(R.string.login_create_account_error)
- }
+ onError { error ->
+ error.printStackTrace()
+ if (!isFinishing && !isDestroyed) {
+ binding.llLoginProgress.postEx {
+ visibility = View.GONE
+ }
+ binding.llLoginError.postEx {
+ visibility = View.VISIBLE
+ }
+ binding.tvLoginError.postEx {
+ text = getString(R.string.login_create_account_error)
}
}
}
+ }
}
private fun resultCallback(result: Boolean) {
@@ -424,7 +606,7 @@ class LoginActivity : AppCompatActivity() {
}
override fun onDestroy() {
- compositeDisposable.dispose()
+ compositeDisposable?.dispose()
super.onDestroy()
}
}
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/ManageAccountActivity.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/ManageAccountActivity.kt
index 88d6d8c..5ef3a0e 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/ManageAccountActivity.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/ManageAccountActivity.kt
@@ -1,12 +1,16 @@
package io.gripxtech.odoojsonrpcclient.core.authenticator
-import android.databinding.DataBindingUtil
-import android.support.v7.app.AppCompatActivity
+import android.content.Context
+import android.content.res.Configuration
import android.os.Bundle
-import android.support.v7.app.AppCompatDelegate
-import android.support.v7.widget.LinearLayoutManager
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.app.AppCompatDelegate
+import androidx.databinding.DataBindingUtil
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
import io.gripxtech.odoojsonrpcclient.App
import io.gripxtech.odoojsonrpcclient.R
+import io.gripxtech.odoojsonrpcclient.core.utils.LocaleHelper
import io.gripxtech.odoojsonrpcclient.core.utils.recycler.decorators.VerticalLinearItemDecorator
import io.gripxtech.odoojsonrpcclient.databinding.ActivityManageAccountBinding
import io.gripxtech.odoojsonrpcclient.getOdooUsers
@@ -21,13 +25,28 @@ class ManageAccountActivity : AppCompatActivity() {
}
lateinit var app: App private set
- lateinit var compositeDisposable: CompositeDisposable private set
+ var compositeDisposable: CompositeDisposable? = null
+ private set
lateinit var binding: ActivityManageAccountBinding private set
lateinit var adapter: ManageAccountAdapter private set
+ override fun attachBaseContext(newBase: Context?) {
+ if (newBase != null) {
+ super.attachBaseContext(LocaleHelper.setLocale(newBase))
+ } else {
+ super.attachBaseContext(newBase)
+ }
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ LocaleHelper.setLocale(this)
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
app = application as App
+ compositeDisposable?.dispose()
compositeDisposable = CompositeDisposable()
binding = DataBindingUtil.setContentView(this, R.layout.activity_manage_account)
@@ -40,7 +59,7 @@ class ManageAccountActivity : AppCompatActivity() {
val users = getOdooUsers()
val layoutManager = LinearLayoutManager(
- this, LinearLayoutManager.VERTICAL, false
+ this, RecyclerView.VERTICAL, false
)
binding.rv.layoutManager = layoutManager
binding.rv.addItemDecoration(VerticalLinearItemDecorator(
@@ -52,7 +71,7 @@ class ManageAccountActivity : AppCompatActivity() {
}
override fun onDestroy() {
- compositeDisposable.dispose()
+ compositeDisposable?.dispose()
super.onDestroy()
}
}
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/ManageAccountAdapter.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/ManageAccountAdapter.kt
index 9548530..648e918 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/ManageAccountAdapter.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/ManageAccountAdapter.kt
@@ -1,10 +1,10 @@
package io.gripxtech.odoojsonrpcclient.core.authenticator
-import android.support.v4.content.ContextCompat
-import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.core.content.ContextCompat
+import androidx.recyclerview.widget.RecyclerView
import io.gripxtech.odoojsonrpcclient.*
import io.gripxtech.odoojsonrpcclient.core.Odoo
import io.gripxtech.odoojsonrpcclient.core.OdooUser
@@ -139,7 +139,7 @@ class ManageAccountAdapter(
Odoo.authenticate(user.login, user.password, user.database) {
onSubscribe { disposable ->
- activity.compositeDisposable.add(disposable)
+ activity.compositeDisposable?.add(disposable)
}
onNext { response ->
@@ -184,12 +184,12 @@ class ManageAccountAdapter(
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeEx {
- onSubscribe { _ ->
+ onSubscribe {
// Must be complete, not dispose in between
// compositeDisposable.add(d)
}
- onNext { _ ->
+ onNext {
activity.restartApp()
}
@@ -211,12 +211,12 @@ class ManageAccountAdapter(
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeEx {
- onSubscribe { _ ->
+ onSubscribe {
// Must be complete, not dispose in between
// compositeDisposable.add(d)
}
- onNext { _ ->
+ onNext {
activity.restartApp()
}
@@ -238,7 +238,7 @@ class ManageAccountAdapter(
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeEx {
- onSubscribe { _ ->
+ onSubscribe {
// Must be complete, not dispose in between
// compositeDisposable.add(d)
}
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/ManageAccountViewHolder.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/ManageAccountViewHolder.kt
index f784531..1324ff7 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/ManageAccountViewHolder.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/ManageAccountViewHolder.kt
@@ -1,6 +1,6 @@
package io.gripxtech.odoojsonrpcclient.core.authenticator
-import android.support.v7.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView
import io.gripxtech.odoojsonrpcclient.databinding.ItemViewManageAccountBinding
class ManageAccountViewHolder(
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/ProfileActivity.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/ProfileActivity.kt
index 644d96d..ec86afe 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/ProfileActivity.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/ProfileActivity.kt
@@ -1,10 +1,13 @@
package io.gripxtech.odoojsonrpcclient.core.authenticator
-import android.databinding.DataBindingUtil
-import android.support.v7.app.AppCompatActivity
+import android.content.Context
+import android.content.res.Configuration
import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
import io.gripxtech.odoojsonrpcclient.App
import io.gripxtech.odoojsonrpcclient.R
+import io.gripxtech.odoojsonrpcclient.core.utils.LocaleHelper
import io.gripxtech.odoojsonrpcclient.databinding.ActivityProfileBinding
import io.gripxtech.odoojsonrpcclient.getActiveOdooUser
@@ -13,6 +16,19 @@ class ProfileActivity : AppCompatActivity() {
private lateinit var app: App
private lateinit var binding: ActivityProfileBinding
+ override fun attachBaseContext(newBase: Context?) {
+ if (newBase != null) {
+ super.attachBaseContext(LocaleHelper.setLocale(newBase))
+ } else {
+ super.attachBaseContext(newBase)
+ }
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ LocaleHelper.setLocale(this)
+ }
+
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
app = application as App
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/SplashActivity.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/SplashActivity.kt
index e5183dd..0506ed5 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/SplashActivity.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/authenticator/SplashActivity.kt
@@ -1,31 +1,58 @@
package io.gripxtech.odoojsonrpcclient.core.authenticator
+import android.content.Context
import android.content.DialogInterface
import android.content.Intent
+import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
-import android.support.v4.app.ActivityCompat
-import android.support.v7.app.AppCompatActivity
import android.text.Html
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.app.ActivityCompat
import io.gripxtech.odoojsonrpcclient.*
import io.gripxtech.odoojsonrpcclient.core.Odoo
import io.gripxtech.odoojsonrpcclient.core.OdooUser
import io.gripxtech.odoojsonrpcclient.core.entities.session.authenticate.AuthenticateResult
+import io.gripxtech.odoojsonrpcclient.core.utils.LocaleHelper
import io.gripxtech.odoojsonrpcclient.core.utils.android.ktx.subscribeEx
import io.reactivex.Observable
+import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
-import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
class SplashActivity : AppCompatActivity() {
private lateinit var app: App
- private lateinit var compositeDisposable: CompositeDisposable
+ private var compositeDisposable: CompositeDisposable? = null
+
+ override fun attachBaseContext(newBase: Context?) {
+ if (newBase != null) {
+ super.attachBaseContext(LocaleHelper.setLocale(newBase))
+ } else {
+ super.attachBaseContext(newBase)
+ }
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ LocaleHelper.setLocale(this)
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+
+ if (!isTaskRoot
+ && intent.hasCategory(Intent.CATEGORY_LAUNCHER)
+ && intent.action != null
+ && intent.action == Intent.ACTION_MAIN
+ ) {
+ finish()
+ return
+ }
+
app = application as App
+ compositeDisposable?.dispose()
compositeDisposable = CompositeDisposable()
}
@@ -40,29 +67,65 @@ class SplashActivity : AppCompatActivity() {
Odoo.user = user
Odoo.check {
onSubscribe { disposable ->
- compositeDisposable.add(disposable)
+ compositeDisposable?.add(disposable)
}
onNext { response ->
- if (response.isSuccessful && response.body()!!.isSuccessful) {
- startMainActivity()
+ if (response.isSuccessful) {
+ val check = response.body()!!
+ if (check.isSuccessful) {
+ app.cookiePrefs.setCookies(Odoo.pendingAuthenticateCookies)
+ startMainActivity()
+ } else {
+ val odooError = check.odooError
+ showMessage(
+ title = getString(R.string.server_request_error, response.code(), response.message()),
+ message = check.errorMessage,
+ positiveButton = getString(R.string.login_again),
+ positiveButtonListener = DialogInterface.OnClickListener { _, _ ->
+ authenticate(user)
+ },
+ showNegativeButton = true,
+ negativeButton = getString(R.string.report_feedback),
+ negativeButtonListener = DialogInterface.OnClickListener { _, _ ->
+ val intent = emailIntent(
+ address = arrayOf(getString(R.string.preference_contact_summary)),
+ cc = arrayOf(),
+ subject = "${getString(R.string.app_name)} ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE}) " +
+ getString(R.string.report_feedback),
+ body = "Name: ${odooError.data.name}\n\n" +
+ "Message: ${odooError.data.message}\n\n" +
+ "Exception Type: ${odooError.data.exceptionType}\n\n" +
+ "Arguments: ${odooError.data.arguments}\n\n" +
+ "Debug: ${odooError.data.debug}\n\n"
+ )
+ try {
+ startActivity(intent)
+ } catch (e: Exception) {
+ e.printStackTrace()
+ showMessage(message = getString(R.string.preference_error_email_intent))
+ }
+ },
+ showNeutralButton = true,
+ neutralButton = getString(R.string.preference_logout_title),
+ neutralButtonListener = DialogInterface.OnClickListener { _, _ ->
+ logoutApp()
+ }
+ )
+ }
} else {
val errorBody = response.errorBody()?.string()
?: getString(R.string.generic_error)
@Suppress("DEPRECATION")
- val message: CharSequence = if (errorBody.contains("jsonrpc", ignoreCase = true)) {
- response.body()?.errorMessage ?: getString(R.string.generic_error)
- } else {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
- Html.fromHtml(errorBody, Html.FROM_HTML_MODE_COMPACT)
- else
- Html.fromHtml(errorBody)
- }
+ val message: CharSequence = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+ Html.fromHtml(errorBody, Html.FROM_HTML_MODE_COMPACT)
+ else
+ Html.fromHtml(errorBody)
showMessage(
title = getString(R.string.server_request_error, response.code(), response.message()),
message = message,
- positiveButton = getString(R.string.try_again),
+ positiveButton = getString(R.string.login_again),
positiveButtonListener = DialogInterface.OnClickListener { _, _ ->
authenticate(user)
},
@@ -70,7 +133,12 @@ class SplashActivity : AppCompatActivity() {
negativeButton = getString(R.string.quit),
negativeButtonListener = DialogInterface.OnClickListener { _, _ ->
ActivityCompat.finishAffinity(this@SplashActivity)
- }
+ },
+ showNeutralButton = true,
+ neutralButton = getString(R.string.preference_logout_title),
+ neutralButtonListener = DialogInterface.OnClickListener { _, _ ->
+ logoutApp()
+ }
)
}
}
@@ -88,10 +156,26 @@ class SplashActivity : AppCompatActivity() {
}
}
+ private fun logoutApp() {
+ Single.fromCallable {
+ for (odooUser in getOdooUsers()) {
+ deleteOdooUser(odooUser)
+ }
+ }.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribeEx {
+ onSuccess {
+ restartApp()
+ }
+
+ onError { error ->
+ error.printStackTrace()
+ }
+ }
+ }
+
private fun authenticate(user: OdooUser) {
Odoo.authenticate(login = user.login, password = user.password, database = user.database) {
onSubscribe { disposable ->
- compositeDisposable.add(disposable)
+ compositeDisposable?.add(disposable)
}
onNext { response ->
@@ -101,8 +185,7 @@ class SplashActivity : AppCompatActivity() {
createAccount(authenticateResult = authenticate.result, user = user)
} else {
// logoutOdooUser(user)
- deleteOdooUser(user)
- restartApp()
+ logoutApp()
}
} else {
showServerErrorMessage(response, positiveButtonListener = DialogInterface.OnClickListener { _, _ ->
@@ -140,7 +223,7 @@ class SplashActivity : AppCompatActivity() {
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeEx {
- onSubscribe { _: Disposable ->
+ onSubscribe {
// Must be complete, not dispose in between
// compositeDisposable.add(d)
}
@@ -171,7 +254,7 @@ class SplashActivity : AppCompatActivity() {
}
override fun onDestroy() {
- compositeDisposable.dispose()
+ compositeDisposable?.dispose()
super.onDestroy()
}
}
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/entities/Many2One.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/entities/Many2One.kt
index c8e52aa..3e815df 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/entities/Many2One.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/entities/Many2One.kt
@@ -1,11 +1,14 @@
package io.gripxtech.odoojsonrpcclient.core.entities
+import android.os.Parcel
+import android.os.Parcelable
import com.google.gson.JsonElement
import io.gripxtech.odoojsonrpcclient.toJsonElement
+import io.gripxtech.odoojsonrpcclient.toStringList
data class Many2One(
private val jsonElement: JsonElement
-) {
+) : Parcelable {
val isManyToOne: Boolean
get() = jsonElement.isJsonArray && jsonElement.asJsonArray.size() == 2
@@ -20,4 +23,28 @@ data class Many2One(
set(value) {
if (isManyToOne) jsonElement.asJsonArray.set(1, value.toJsonElement())
}
+
+ constructor(parcel: Parcel) : this(
+ arrayListOf().apply {
+ parcel.readStringList(this)
+ }.toJsonElement()
+ )
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeStringList(toStringList())
+ }
+
+ override fun describeContents(): Int {
+ return 0
+ }
+
+ companion object CREATOR : Parcelable.Creator {
+ override fun createFromParcel(parcel: Parcel): Many2One {
+ return Many2One(parcel)
+ }
+
+ override fun newArray(size: Int): Array {
+ return arrayOfNulls(size)
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/entities/route/RouteReqBody.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/entities/route/RouteReqBody.kt
index 63bac68..704a17e 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/entities/route/RouteReqBody.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/entities/route/RouteReqBody.kt
@@ -19,5 +19,5 @@ data class RouteReqBody(
@field:Expose
@field:SerializedName("params")
- val params: Map = mapOf()
+ val params: Any = mapOf()
)
\ No newline at end of file
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/entities/session/authenticate/AuthenticateResult.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/entities/session/authenticate/AuthenticateResult.kt
index fc8513a..290bdb5 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/entities/session/authenticate/AuthenticateResult.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/entities/session/authenticate/AuthenticateResult.kt
@@ -90,6 +90,7 @@ data class AuthenticateResult(
putString("database", db)
putString("serverVersion", serverVersion)
putString("isAdmin", admin.toString())
+ putString("isSuperuser", superuser.toString())
putString("id", uid.toString())
putString("partnerId", partnerId.toString())
putString("name", name)
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/preferences/SettingsActivity.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/preferences/SettingsActivity.kt
index 20d2401..b76acc3 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/preferences/SettingsActivity.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/preferences/SettingsActivity.kt
@@ -1,11 +1,14 @@
package io.gripxtech.odoojsonrpcclient.core.preferences
-import android.databinding.DataBindingUtil
+import android.content.Context
+import android.content.res.Configuration
import android.os.Bundle
-import android.support.v7.app.AppCompatActivity
-import android.support.v7.app.AppCompatDelegate
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.app.AppCompatDelegate
+import androidx.databinding.DataBindingUtil
import io.gripxtech.odoojsonrpcclient.App
import io.gripxtech.odoojsonrpcclient.R
+import io.gripxtech.odoojsonrpcclient.core.utils.LocaleHelper
import io.gripxtech.odoojsonrpcclient.databinding.ActivitySettingsBinding
class SettingsActivity : AppCompatActivity() {
@@ -19,6 +22,19 @@ class SettingsActivity : AppCompatActivity() {
private lateinit var app: App
lateinit var binding: ActivitySettingsBinding private set
+ override fun attachBaseContext(newBase: Context?) {
+ if (newBase != null) {
+ super.attachBaseContext(LocaleHelper.setLocale(newBase))
+ } else {
+ super.attachBaseContext(newBase)
+ }
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ LocaleHelper.setLocale(this)
+ }
+
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
app = application as App
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/preferences/SettingsFragment.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/preferences/SettingsFragment.kt
index 14dc43a..2861470 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/preferences/SettingsFragment.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/preferences/SettingsFragment.kt
@@ -4,12 +4,18 @@ import android.content.Intent
import android.net.MailTo
import android.net.Uri
import android.os.Bundle
-import android.support.design.widget.Snackbar
-import android.support.v7.preference.Preference
-import android.support.v7.preference.PreferenceFragmentCompat
-import io.gripxtech.odoojsonrpcclient.BuildConfig
-import io.gripxtech.odoojsonrpcclient.R
-import io.gripxtech.odoojsonrpcclient.getActiveOdooUser
+import androidx.core.app.TaskStackBuilder
+import androidx.preference.ListPreference
+import androidx.preference.Preference
+import androidx.preference.PreferenceFragmentCompat
+import com.google.android.material.snackbar.Snackbar
+import io.gripxtech.odoojsonrpcclient.*
+import io.gripxtech.odoojsonrpcclient.core.authenticator.SplashActivity
+import io.gripxtech.odoojsonrpcclient.core.utils.LocalePrefs
+import io.gripxtech.odoojsonrpcclient.core.utils.android.ktx.subscribeEx
+import io.reactivex.Single
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.schedulers.Schedulers
class SettingsFragment : PreferenceFragmentCompat() {
@@ -20,38 +26,67 @@ class SettingsFragment : PreferenceFragmentCompat() {
private lateinit var activity: SettingsActivity
private lateinit var build: Preference
+ private lateinit var language: ListPreference
private lateinit var organization: Preference
private lateinit var privacy: Preference
private lateinit var contact: Preference
+ private lateinit var logout: Preference
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
activity = getActivity() as SettingsActivity
addPreferencesFromResource(R.xml.preferences)
build = findPreference(getString(R.string.preference_build_key))
+ language = findPreference(getString(R.string.preference_language_key)) as ListPreference
organization = findPreference(getString(R.string.preference_organization_key))
privacy = findPreference(getString(R.string.preference_privacy_policy_key))
contact = findPreference(getString(R.string.preference_contact_key))
+ logout = findPreference(getString(R.string.preference_logout_key))
build.summary = getString(R.string.preference_build_summary, BuildConfig.VERSION_NAME)
- organization.setOnPreferenceClickListener { _: Preference ->
- startActivity(Intent(
+ language.setOnPreferenceChangeListener { _, newValue ->
+ if (newValue is String) {
+ val localePrefs = LocalePrefs(activity)
+ when (newValue) {
+ getString(R.string.language_code_spanish) -> {
+ localePrefs.language = getString(R.string.language_code_spanish)
+ }
+ getString(R.string.language_code_english) -> {
+ localePrefs.language = getString(R.string.language_code_english)
+ }
+ else -> {
+ localePrefs.clear()
+ }
+ }
+ TaskStackBuilder.create(activity)
+ .addNextIntent(Intent(activity, SplashActivity::class.java))
+ .startActivities()
+ }
+ true
+ }
+
+ organization.setOnPreferenceClickListener {
+ startActivity(
+ Intent(
Intent.ACTION_VIEW,
Uri.parse(getString(R.string.preference_organization_website))
- ))
+ )
+ )
true
}
- privacy.setOnPreferenceClickListener { _: Preference ->
- startActivity(Intent(
+ privacy.setOnPreferenceClickListener {
+ startActivity(
+ Intent(
Intent.ACTION_VIEW,
Uri.parse(getString(R.string.preference_privacy_policy_url))
- ))
+ )
+ )
true
}
- contact.setOnPreferenceClickListener { _: Preference ->
+ contact.setOnPreferenceClickListener {
val lclContext = context
val url = ("mailto:" + getString(R.string.preference_contact_summary)
+ "?subject=Contact by " + getString(R.string.app_name) + " user " +
@@ -67,14 +102,33 @@ class SettingsFragment : PreferenceFragmentCompat() {
} catch (e: Exception) {
e.printStackTrace()
Snackbar.make(
- activity.binding.root,
- R.string.preference_error_email_intent,
- Snackbar.LENGTH_LONG
+ activity.binding.root,
+ R.string.preference_error_email_intent,
+ Snackbar.LENGTH_LONG
).show()
}
true
}
+
+ logout.setOnPreferenceClickListener {
+ Single.fromCallable {
+ for (odooUser in activity.getOdooUsers()) {
+ activity.deleteOdooUser(odooUser)
+ }
+ }.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribeEx {
+ onSuccess {
+ TaskStackBuilder.create(activity)
+ .addNextIntent(Intent(activity, SplashActivity::class.java))
+ .startActivities()
+ }
+
+ onError { error ->
+ error.printStackTrace()
+ }
+ }
+ true
+ }
}
private fun emailIntent(address: Array, cc: Array, subject: String, body: String): Intent {
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/ClonedCookie.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/ClonedCookie.kt
index 588b57b..ccf4d4f 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/ClonedCookie.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/ClonedCookie.kt
@@ -1,12 +1,12 @@
package io.gripxtech.odoojsonrpcclient.core.utils
-import android.annotation.SuppressLint
+import android.os.Parcelable
import com.google.gson.annotations.Expose
import com.google.gson.annotations.SerializedName
-import io.mironov.smuggler.AutoParcelable
+import kotlinx.android.parcel.Parcelize
import okhttp3.Cookie
-@SuppressLint("ParcelCreator")
+@Parcelize
data class ClonedCookie(
@field:Expose
@@ -45,7 +45,7 @@ data class ClonedCookie(
@field:SerializedName("hostOnly")
private val hostOnly: Boolean
-) : AutoParcelable {
+) : Parcelable {
companion object {
fun fromCookie(cookie: Cookie) = ClonedCookie(
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/CookiePrefs.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/CookiePrefs.kt
index a0a641c..8529339 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/CookiePrefs.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/CookiePrefs.kt
@@ -3,8 +3,11 @@ package io.gripxtech.odoojsonrpcclient.core.utils
import android.content.Context
import com.google.gson.reflect.TypeToken
import io.gripxtech.odoojsonrpcclient.getActiveOdooUser
+import io.gripxtech.odoojsonrpcclient.getCookies
import io.gripxtech.odoojsonrpcclient.gson
+import io.gripxtech.odoojsonrpcclient.setCookies
import okhttp3.Cookie
+import timber.log.Timber
class CookiePrefs(context: Context) : Prefs(CookiePrefs.TAG, context) {
@@ -17,13 +20,15 @@ class CookiePrefs(context: Context) : Prefs(CookiePrefs.TAG, context) {
fun getCookies(): ArrayList {
val activeUser = context.getActiveOdooUser()
if (activeUser != null) {
- val cookiesStr = getString(activeUser.androidName)
+ // val cookiesStr = getString(activeUser.androidName)
+ val cookiesStr = context.getCookies(activeUser)
if (cookiesStr.isNotEmpty()) {
val clonedCookies: ArrayList = gson.fromJson(cookiesStr, type)
val cookies = arrayListOf()
for (clonedCookie in clonedCookies) {
cookies += clonedCookie.toCookie()
}
+ Timber.i("getCookies() returned $cookies")
return cookies
}
}
@@ -31,6 +36,7 @@ class CookiePrefs(context: Context) : Prefs(CookiePrefs.TAG, context) {
}
fun setCookies(cookies: ArrayList) {
+ Timber.i("setCookies() called with $cookies")
val clonedCookies = arrayListOf()
for (cookie in cookies) {
clonedCookies += ClonedCookie.fromCookie(cookie)
@@ -38,7 +44,8 @@ class CookiePrefs(context: Context) : Prefs(CookiePrefs.TAG, context) {
val cookiesStr = gson.toJson(clonedCookies, type)
val activeUser = context.getActiveOdooUser()
if (activeUser != null) {
- putString(activeUser.androidName, cookiesStr)
+ context.setCookies(activeUser, cookiesStr)
+ // putString(activeUser.androidName, cookiesStr)
}
}
}
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/LetterTileProvider.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/LetterTileProvider.kt
index 1fbdc4d..5099f7c 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/LetterTileProvider.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/LetterTileProvider.kt
@@ -4,9 +4,9 @@ import android.content.Context
import android.content.res.Resources
import android.graphics.*
import android.graphics.drawable.BitmapDrawable
-import android.support.annotation.DrawableRes
-import android.support.v4.content.ContextCompat
import android.text.TextPaint
+import androidx.annotation.DrawableRes
+import androidx.core.content.ContextCompat
import io.gripxtech.odoojsonrpcclient.R
import java.io.ByteArrayOutputStream
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/LocaleHelper.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/LocaleHelper.kt
new file mode 100644
index 0000000..7394f28
--- /dev/null
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/LocaleHelper.kt
@@ -0,0 +1,31 @@
+package io.gripxtech.odoojsonrpcclient.core.utils
+
+import android.content.Context
+import android.content.res.Configuration
+import java.util.*
+
+object LocaleHelper {
+
+ fun setLocale(context: Context): Context = setNewLocale(context, getLanguage(context))
+
+ private fun setNewLocale(context: Context, language: String): Context {
+ persistLanguage(context, language)
+ return updateResources(context, language)
+ }
+
+ private fun getLanguage(context: Context): String = LocalePrefs(context).language
+
+ private fun persistLanguage(context: Context, language: String) {
+ LocalePrefs(context).language = language
+ }
+
+ private fun updateResources(context: Context, language: String): Context {
+ val locale = Locale(language)
+ Locale.setDefault(locale)
+
+ val res = context.resources
+ val config = Configuration(res.configuration)
+ config.setLocale(locale)
+ return context.createConfigurationContext(config)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/LocalePrefs.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/LocalePrefs.kt
new file mode 100644
index 0000000..eb3954a
--- /dev/null
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/LocalePrefs.kt
@@ -0,0 +1,17 @@
+package io.gripxtech.odoojsonrpcclient.core.utils
+
+import android.content.Context
+import androidx.core.os.ConfigurationCompat
+
+class LocalePrefs(context: Context) : Prefs(LocalePrefs.TAG, context) {
+
+ companion object {
+ const val TAG = "LocalePrefs"
+
+ private const val Language = "Language"
+ }
+
+ var language: String
+ get() = getString(Language, ConfigurationCompat.getLocales(context.resources.configuration)[0].language)
+ set(value) = putString(Language, value)
+}
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/NavHeaderViewHolder.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/NavHeaderViewHolder.kt
index 31e8003..e693b94 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/NavHeaderViewHolder.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/NavHeaderViewHolder.kt
@@ -1,39 +1,44 @@
package io.gripxtech.odoojsonrpcclient.core.utils
-import android.support.constraint.ConstraintLayout
import android.util.Base64
import android.view.View
import android.widget.ImageView
import android.widget.TextView
-import de.hdodenhof.circleimageview.CircleImageView
+import androidx.constraintlayout.widget.ConstraintLayout
import io.gripxtech.odoojsonrpcclient.App
-import io.gripxtech.odoojsonrpcclient.GlideApp
+import io.gripxtech.odoojsonrpcclient.GlideRequests
import io.gripxtech.odoojsonrpcclient.R
import io.gripxtech.odoojsonrpcclient.core.OdooUser
import io.gripxtech.odoojsonrpcclient.trimFalse
-class NavHeaderViewHolder(view: View) {
- val pic: CircleImageView = view.findViewById(R.id.userImage)
- val name: TextView = view.findViewById(R.id.header_name)
- val email: TextView = view.findViewById(R.id.header_details)
- val menuToggle: ConstraintLayout = view.findViewById(R.id.menuToggle)
- val menuToggleImage: ImageView = view.findViewById(R.id.ivDropdown)
+class NavHeaderViewHolder(
+ view: View
+) {
+ private val pic: ImageView = view.findViewById(R.id.userImage)
+ private val name: TextView = view.findViewById(R.id.header_name)
+ private val email: TextView = view.findViewById(R.id.header_details)
+ private val menuToggle: ConstraintLayout = view.findViewById(R.id.menuToggle)
+ private val menuToggleImage: ImageView = view.findViewById(R.id.ivDropdown)
- fun setUser(user: OdooUser) {
+ fun setUser(user: OdooUser, glideRequests: GlideRequests) {
name.text = user.name
email.text = user.login
if (user.imageSmall.trimFalse().isNotEmpty()) {
val byteArray = Base64.decode(user.imageSmall, Base64.DEFAULT)
- GlideApp.with(pic.context)
- .asBitmap()
- .load(byteArray)
- .into(pic)
+ glideRequests
+ .asBitmap()
+ .load(byteArray)
+ .dontAnimate()
+ .circleCrop()
+ .into(pic)
} else {
- GlideApp.with(pic.context)
- .asBitmap()
- .load((pic.context.applicationContext as App).getLetterTile(user.name))
- .into(pic)
+ glideRequests
+ .asBitmap()
+ .load((pic.context.applicationContext as App).getLetterTile(user.name))
+ .dontAnimate()
+ .circleCrop()
+ .into(pic)
}
}
}
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/OdooFileProvider.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/OdooFileProvider.kt
new file mode 100644
index 0000000..6ee5728
--- /dev/null
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/OdooFileProvider.kt
@@ -0,0 +1,5 @@
+package io.gripxtech.odoojsonrpcclient.core.utils
+
+import androidx.core.content.FileProvider
+
+class OdooFileProvider : FileProvider()
\ No newline at end of file
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/Prefs.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/Prefs.kt
index 5ae2f83..0276fce 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/Prefs.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/Prefs.kt
@@ -23,11 +23,13 @@ abstract class Prefs(val name: String, val context: Context) {
protected fun putLong(key: String, value: Long) = preferences.edit().putLong(key, value).apply()
- protected fun getString(key: String, defValue: String = ""): String = preferences.getString(key, defValue)
+ protected fun getString(key: String, defValue: String = ""): String =
+ preferences.getString(key, defValue) ?: defValue
protected fun putString(key: String, value: String) = preferences.edit().putString(key, value).apply()
- protected fun getStringSet(key: String, defValue: MutableSet = mutableSetOf()): MutableSet = preferences.getStringSet(key, defValue)
+ protected fun getStringSet(key: String, defValue: MutableSet = mutableSetOf()): MutableSet =
+ preferences.getStringSet(key, defValue) ?: defValue
protected fun putStringSet(key: String, value: MutableSet) = preferences.edit().putStringSet(key, value).apply()
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/Retrofit2Helper.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/Retrofit2Helper.kt
index e419e82..ff815ad 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/Retrofit2Helper.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/Retrofit2Helper.kt
@@ -12,8 +12,8 @@ import timber.log.Timber
class Retrofit2Helper(
- _protocol: Retrofit2Helper.Companion.Protocol,
- _host: String
+ _protocol: Retrofit2Helper.Companion.Protocol,
+ _host: String
) {
companion object {
const val TAG = "Retrofit2Helper"
@@ -43,56 +43,58 @@ class Retrofit2Helper(
get() {
if (_retrofit == null) {
_retrofit = Retrofit.Builder()
- .baseUrl(when (protocol) {
+ .baseUrl(
+ when (protocol) {
Retrofit2Helper.Companion.Protocol.HTTP -> {
"http://"
}
Retrofit2Helper.Companion.Protocol.HTTPS -> {
"https://"
}
- } + host)
- .client(client)
- .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
- .addConverterFactory(GsonConverterFactory.create(gson))
- .build()
+ } + host
+ )
+ .client(client)
+ .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
+ .addConverterFactory(GsonConverterFactory.create(gson))
+ .build()
}
return _retrofit!!
}
- val client: OkHttpClient
+ private val client: OkHttpClient
get() = OkHttpClient()
- .newBuilder()
- .cookieJar(object : CookieJar {
+ .newBuilder()
+ .cookieJar(object : CookieJar {
- private var cookies: MutableList? = Retrofit2Helper.app.cookiePrefs.getCookies()
+ private var cookies: MutableList? = Retrofit2Helper.app.cookiePrefs.getCookies()
- override fun saveFromResponse(url: HttpUrl?, cookies: MutableList?) {
- if (url.toString().contains("/web/session/authenticate")) {
- this.cookies = cookies
- if (cookies != null) {
- Odoo.pendingAuthenticateCookies.clear()
- Odoo.pendingAuthenticateCookies.addAll(cookies)
- }
+ override fun saveFromResponse(url: HttpUrl?, cookies: MutableList?) {
+ if (url.toString().contains("/web/session/authenticate") || url.toString().contains("web/session/check")) {
+ this.cookies = cookies
+ if (cookies != null) {
+ Odoo.pendingAuthenticateCookies.clear()
+ Odoo.pendingAuthenticateCookies.addAll(cookies)
}
}
+ }
- override fun loadForRequest(url: HttpUrl?): MutableList? =
- cookies
- })
- .addInterceptor { chain: Interceptor.Chain? ->
- val original = chain!!.request()
+ override fun loadForRequest(url: HttpUrl?): MutableList? =
+ cookies
+ })
+ .addInterceptor { chain: Interceptor.Chain? ->
+ val original = chain!!.request()
- val request = original.newBuilder()
- .header("User-Agent", android.os.Build.MODEL)
- .method(original.method(), original.body())
- .build()
+ val request = original.newBuilder()
+ .header("User-Agent", android.os.Build.MODEL)
+ .method(original.method(), original.body())
+ .build()
- chain.proceed(request)
- }
- .addInterceptor(HttpLoggingInterceptor {
- Timber.tag("OkHttp").d(it)
- }.apply {
- level = HttpLoggingInterceptor.Level.BODY
- })
- .build()
+ chain.proceed(request)
+ }
+ .addInterceptor(HttpLoggingInterceptor {
+ Timber.tag("OkHttp").d(it)
+ }.apply {
+ level = HttpLoggingInterceptor.Level.BODY
+ })
+ .build()
}
\ No newline at end of file
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/android/ktx/AndroidKtx.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/android/ktx/AndroidKtx.kt
index a26eeea..380f83f 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/android/ktx/AndroidKtx.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/android/ktx/AndroidKtx.kt
@@ -3,7 +3,7 @@ package io.gripxtech.odoojsonrpcclient.core.utils.android.ktx
import android.view.View
import android.widget.AdapterView
import android.widget.TextView
-import io.reactivex.Observable
+import io.reactivex.*
inline fun AdapterView<*>.setOnItemSelectedListenerEx(listener: OnItemSelectedListenerEx.() -> Unit) {
val listenerEx = OnItemSelectedListenerEx()
@@ -26,3 +26,27 @@ inline fun Observable.subscribeEx(crossinline observer: ObserverEx.()
observerEx.observer()
subscribe(observerEx)
}
+
+inline fun Flowable.subscribeEx(crossinline subscriber: FlowableSubscriberEx.() -> Unit) {
+ val subscriberEx = FlowableSubscriberEx()
+ subscriberEx.subscriber()
+ subscribe(subscriberEx)
+}
+
+inline fun Single.subscribeEx(crossinline observer: SingleObserverEx.() -> Unit) {
+ val observerEx = SingleObserverEx()
+ observerEx.observer()
+ subscribe(observerEx)
+}
+
+inline fun Maybe.subscribeEx(crossinline observer: MaybeObserverEx.() -> Unit) {
+ val observerEx = MaybeObserverEx()
+ observerEx.observer()
+ subscribe(observerEx)
+}
+
+inline fun Completable.subscribeEx(crossinline observer: CompletableObserverEx.() -> Unit) {
+ val observerEx = CompletableObserverEx()
+ observerEx.observer()
+ subscribe(observerEx)
+}
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/android/ktx/CompletableObserverEx.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/android/ktx/CompletableObserverEx.kt
new file mode 100644
index 0000000..2e2e15c
--- /dev/null
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/android/ktx/CompletableObserverEx.kt
@@ -0,0 +1,45 @@
+package io.gripxtech.odoojsonrpcclient.core.utils.android.ktx
+
+import io.reactivex.CompletableObserver
+import io.reactivex.disposables.Disposable
+import timber.log.Timber
+
+class CompletableObserverEx : CompletableObserver {
+
+ private var subscribe: ((disposable: Disposable) -> Unit) = {
+ Timber.d("onSubscribe() called")
+ }
+
+ fun onSubscribe(subscribe: (disposable: Disposable) -> Unit) {
+ this.subscribe = subscribe
+ }
+
+ override fun onSubscribe(disposable: Disposable) {
+ this.subscribe.invoke(disposable)
+ }
+
+ private var error: ((error: Throwable) -> Unit) = {
+ Timber.e("onError() called: ${it::class.java.simpleName}: ${it.message}")
+ it.printStackTrace()
+ }
+
+ fun onError(error: (error: Throwable) -> Unit) {
+ this.error = error
+ }
+
+ override fun onError(error: Throwable) {
+ this.error.invoke(error)
+ }
+
+ private var complete: (() -> Unit) = {
+ Timber.d("onComplete() called")
+ }
+
+ fun onComplete(complete: () -> Unit) {
+ this.complete = complete
+ }
+
+ override fun onComplete() {
+ this.complete.invoke()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/android/ktx/FlowableSubscriberEx.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/android/ktx/FlowableSubscriberEx.kt
new file mode 100644
index 0000000..d2eb816
--- /dev/null
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/android/ktx/FlowableSubscriberEx.kt
@@ -0,0 +1,57 @@
+package io.gripxtech.odoojsonrpcclient.core.utils.android.ktx
+
+import io.reactivex.FlowableSubscriber
+import org.reactivestreams.Subscription
+import timber.log.Timber
+
+class FlowableSubscriberEx : FlowableSubscriber {
+
+ private var subscribe: ((subscription: Subscription) -> Unit) = {
+ Timber.d("onSubscribe() called")
+ }
+
+ fun onSubscribe(subscribe: (subscription: Subscription) -> Unit) {
+ this.subscribe = subscribe
+ }
+
+ override fun onSubscribe(subscription: Subscription) {
+ this.subscribe.invoke(subscription)
+ }
+
+ private var next: ((response: T) -> Unit) = {
+ Timber.d("onNext() called: response is $it")
+ }
+
+ fun onNext(next: (response: T) -> Unit) {
+ this.next = next
+ }
+
+ override fun onNext(response: T) {
+ this.next.invoke(response)
+ }
+
+ private var error: ((error: Throwable) -> Unit) = {
+ Timber.e("onError() called: ${it::class.java.simpleName}: ${it.message}")
+ it.printStackTrace()
+ }
+
+ fun onError(error: (error: Throwable) -> Unit) {
+ this.error = error
+ }
+
+ override fun onError(error: Throwable) {
+ this.error.invoke(error)
+ }
+
+ private var complete: (() -> Unit) = {
+ Timber.d("onComplete() called")
+ }
+
+ fun onComplete(complete: () -> Unit) {
+ this.complete = complete
+ }
+
+ override fun onComplete() {
+ this.complete.invoke()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/android/ktx/MaybeObserverEx.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/android/ktx/MaybeObserverEx.kt
new file mode 100644
index 0000000..7839c15
--- /dev/null
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/android/ktx/MaybeObserverEx.kt
@@ -0,0 +1,57 @@
+package io.gripxtech.odoojsonrpcclient.core.utils.android.ktx
+
+import io.reactivex.MaybeObserver
+import io.reactivex.disposables.Disposable
+import timber.log.Timber
+
+class MaybeObserverEx : MaybeObserver {
+
+ private var subscribe: ((disposable: Disposable) -> Unit) = {
+ Timber.d("onSubscribe() called")
+ }
+
+ fun onSubscribe(subscribe: (disposable: Disposable) -> Unit) {
+ this.subscribe = subscribe
+ }
+
+ override fun onSubscribe(disposable: Disposable) {
+ this.subscribe.invoke(disposable)
+ }
+
+ private var success: ((response: T) -> Unit) = {
+ Timber.d("onNext() called: response is $it")
+ }
+
+ fun onSuccess(success: (response: T) -> Unit) {
+ this.success = success
+ }
+
+ override fun onSuccess(response: T) {
+ this.success.invoke(response)
+ }
+
+ private var error: ((error: Throwable) -> Unit) = {
+ Timber.e("onError() called: ${it::class.java.simpleName}: ${it.message}")
+ it.printStackTrace()
+ }
+
+ fun onError(error: (error: Throwable) -> Unit) {
+ this.error = error
+ }
+
+ override fun onError(error: Throwable) {
+ this.error.invoke(error)
+ }
+
+ private var complete: (() -> Unit) = {
+ Timber.d("onComplete() called")
+ }
+
+ fun onComplete(complete: () -> Unit) {
+ this.complete = complete
+ }
+
+ override fun onComplete() {
+ this.complete.invoke()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/android/ktx/SingleObserverEx.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/android/ktx/SingleObserverEx.kt
new file mode 100644
index 0000000..e082fcd
--- /dev/null
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/android/ktx/SingleObserverEx.kt
@@ -0,0 +1,45 @@
+package io.gripxtech.odoojsonrpcclient.core.utils.android.ktx
+
+import io.reactivex.SingleObserver
+import io.reactivex.disposables.Disposable
+import timber.log.Timber
+
+class SingleObserverEx : SingleObserver {
+
+ private var subscribe: ((disposable: Disposable) -> Unit) = {
+ Timber.d("onSubscribe() called")
+ }
+
+ fun onSubscribe(subscribe: (disposable: Disposable) -> Unit) {
+ this.subscribe = subscribe
+ }
+
+ override fun onSubscribe(disposable: Disposable) {
+ this.subscribe.invoke(disposable)
+ }
+
+ private var success: ((response: T) -> Unit) = {
+ Timber.d("onNext() called: response is $it")
+ }
+
+ fun onSuccess(success: (response: T) -> Unit) {
+ this.success = success
+ }
+
+ override fun onSuccess(response: T) {
+ this.success.invoke(response)
+ }
+
+ private var error: ((error: Throwable) -> Unit) = {
+ Timber.e("onError() called: ${it::class.java.simpleName}: ${it.message}")
+ it.printStackTrace()
+ }
+
+ fun onError(error: (error: Throwable) -> Unit) {
+ this.error = error
+ }
+
+ override fun onError(error: Throwable) {
+ this.error.invoke(error)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/RecyclerBaseAdapter.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/RecyclerBaseAdapter.kt
index 337a37e..7ef6cc5 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/RecyclerBaseAdapter.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/RecyclerBaseAdapter.kt
@@ -1,13 +1,13 @@
package io.gripxtech.odoojsonrpcclient.core.utils.recycler
-import android.databinding.DataBindingUtil
-import android.support.annotation.DrawableRes
-import android.support.v7.widget.LinearLayoutManager
-import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.Filter
import android.widget.Filterable
+import androidx.annotation.DrawableRes
+import androidx.databinding.DataBindingUtil
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
import io.gripxtech.odoojsonrpcclient.R
import io.gripxtech.odoojsonrpcclient.core.utils.recycler.entities.*
import io.gripxtech.odoojsonrpcclient.databinding.ItemViewRecyclerEmptyBinding
@@ -188,9 +188,9 @@ abstract class RecyclerBaseAdapter(
val layoutManager = recyclerView.layoutManager
if (layoutManager != null && layoutManager is LinearLayoutManager) {
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
- override fun onScrolled(lclRecyclerView: RecyclerView?, dx: Int, dy: Int) {
+ override fun onScrolled(lclRecyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
- if (lclRecyclerView != null && pvtMoreListener != null || pvtLessListener != null) {
+ if (pvtMoreListener != null || pvtLessListener != null) {
val totalItemCount = layoutManager.itemCount
val lastVisibleItem = layoutManager.findLastVisibleItemPosition()
val firstVisibleItem = layoutManager.findFirstVisibleItemPosition()
@@ -199,7 +199,7 @@ abstract class RecyclerBaseAdapter(
if (pvtMoreListener != null) {
synchronized(this) {
isMoreLoading = true
- lclRecyclerView?.post {
+ lclRecyclerView.post {
hideMore()
showMore()
val lclPvtMoreListener = pvtMoreListener
@@ -211,7 +211,7 @@ abstract class RecyclerBaseAdapter(
if (pvtLessListener != null) {
synchronized(this) {
isMoreLoading = true
- lclRecyclerView?.post {
+ lclRecyclerView.post {
hideLess()
showLess()
val lclPvtLessListener = pvtLessListener
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/decorators/GridItemDecorator.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/decorators/GridItemDecorator.kt
index 3242a5c..bc33d44 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/decorators/GridItemDecorator.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/decorators/GridItemDecorator.kt
@@ -1,52 +1,49 @@
package io.gripxtech.odoojsonrpcclient.core.utils.recycler.decorators
import android.graphics.Rect
-import android.support.v7.widget.RecyclerView
import android.view.View
+import androidx.recyclerview.widget.RecyclerView
class GridItemDecorator(
- private val sizeGridSpacingPx: Int,
- private val gridSize: Int
+ private val sizeGridSpacingPx: Int,
+ private val gridSize: Int
) : RecyclerView.ItemDecoration() {
private var needLeftSpacing = false
- override fun getItemOffsets(outRect: Rect?, view: View?, parent: RecyclerView?, state: RecyclerView.State?) =
- if (outRect != null && view != null && parent != null) {
- val frameWidth: Int = ((parent.width - sizeGridSpacingPx.toFloat() * (gridSize - 1)) / gridSize).toInt()
- val padding: Int = parent.width / gridSize - frameWidth
- val itemPosition: Int = (view.layoutParams as RecyclerView.LayoutParams).viewAdapterPosition
- if (itemPosition < gridSize) {
- outRect.top = 0
- } else {
- outRect.top = sizeGridSpacingPx
- }
- if (itemPosition % gridSize == 0) {
- outRect.left = 0
- outRect.right = padding
- needLeftSpacing = true
- } else if ((itemPosition + 1) % gridSize == 0) {
- needLeftSpacing = false
- outRect.right = 0
- outRect.left = padding
- } else if (needLeftSpacing) {
- needLeftSpacing = false
- outRect.left = sizeGridSpacingPx - padding
- if ((itemPosition + 2) % gridSize == 0) {
- outRect.right = sizeGridSpacingPx - padding
- } else {
- outRect.right = sizeGridSpacingPx / 2
- }
- } else if ((itemPosition + 2) % gridSize == 0) {
- needLeftSpacing = false
- outRect.left = sizeGridSpacingPx / 2
- outRect.right = sizeGridSpacingPx - padding
- } else {
- needLeftSpacing = false
- outRect.left = sizeGridSpacingPx / 2
- outRect.right = sizeGridSpacingPx / 2
- }
- outRect.bottom = 0
+ override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
+ val frameWidth: Int = ((parent.width - sizeGridSpacingPx.toFloat() * (gridSize - 1)) / gridSize).toInt()
+ val padding: Int = parent.width / gridSize - frameWidth
+ val itemPosition: Int = (view.layoutParams as RecyclerView.LayoutParams).viewAdapterPosition
+ if (itemPosition < gridSize) {
+ outRect.top = 0
+ } else {
+ outRect.top = sizeGridSpacingPx
+ }
+ if (itemPosition % gridSize == 0) {
+ outRect.left = 0
+ outRect.right = padding
+ needLeftSpacing = true
+ } else if ((itemPosition + 1) % gridSize == 0) {
+ needLeftSpacing = false
+ outRect.right = 0
+ outRect.left = padding
+ } else if (needLeftSpacing) {
+ needLeftSpacing = false
+ outRect.left = sizeGridSpacingPx - padding
+ if ((itemPosition + 2) % gridSize == 0) {
+ outRect.right = sizeGridSpacingPx - padding
} else {
- super.getItemOffsets(outRect, view, parent, state)
+ outRect.right = sizeGridSpacingPx / 2
}
+ } else if ((itemPosition + 2) % gridSize == 0) {
+ needLeftSpacing = false
+ outRect.left = sizeGridSpacingPx / 2
+ outRect.right = sizeGridSpacingPx - padding
+ } else {
+ needLeftSpacing = false
+ outRect.left = sizeGridSpacingPx / 2
+ outRect.right = sizeGridSpacingPx / 2
+ }
+ outRect.bottom = 0
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/decorators/HorizontalLinearItemDecorator.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/decorators/HorizontalLinearItemDecorator.kt
index 829cb27..5a7dfb6 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/decorators/HorizontalLinearItemDecorator.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/decorators/HorizontalLinearItemDecorator.kt
@@ -1,21 +1,19 @@
package io.gripxtech.odoojsonrpcclient.core.utils.recycler.decorators
import android.graphics.Rect
-import android.support.v7.widget.RecyclerView
import android.view.View
+import androidx.recyclerview.widget.RecyclerView
class HorizontalLinearItemDecorator(
- private val horizontalSpaceHeight: Int
+ private val horizontalSpaceHeight: Int
) : RecyclerView.ItemDecoration() {
- override fun getItemOffsets(outRect: Rect?, view: View?, parent: RecyclerView?, state: RecyclerView.State?) =
- if (outRect != null && view != null && parent != null) {
- if (parent.getChildAdapterPosition(view)
- != parent.adapter.itemCount - 1) {
- outRect.right = horizontalSpaceHeight
- } else {
- }
- } else {
- super.getItemOffsets(outRect, view, parent, state)
- }
+ override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
+ val viewHolder = parent.adapter
+ if (viewHolder != null && parent.getChildAdapterPosition(view) != viewHolder.itemCount - 1) {
+ outRect.right = horizontalSpaceHeight
+ } else {
+ super.getItemOffsets(outRect, view, parent, state)
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/decorators/VerticalLinearItemDecorator.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/decorators/VerticalLinearItemDecorator.kt
index ca5b14a..da44a3a 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/decorators/VerticalLinearItemDecorator.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/decorators/VerticalLinearItemDecorator.kt
@@ -1,21 +1,19 @@
package io.gripxtech.odoojsonrpcclient.core.utils.recycler.decorators
import android.graphics.Rect
-import android.support.v7.widget.RecyclerView
import android.view.View
+import androidx.recyclerview.widget.RecyclerView
class VerticalLinearItemDecorator(
private val verticalSpaceHeight: Int
) : RecyclerView.ItemDecoration() {
- override fun getItemOffsets(outRect: Rect?, view: View?, parent: RecyclerView?, state: RecyclerView.State?) =
- if (outRect != null && view != null && parent != null) {
- if (parent.getChildAdapterPosition(view)
- != parent.adapter.itemCount - 1) {
- outRect.bottom = verticalSpaceHeight
- } else {
- }
- } else {
- super.getItemOffsets(outRect, view, parent, state)
- }
+ override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
+ val viewHolder = parent.adapter
+ if (viewHolder != null && parent.getChildAdapterPosition(view) != viewHolder.itemCount - 1) {
+ outRect.bottom = verticalSpaceHeight
+ } else {
+ super.getItemOffsets(outRect, view, parent, state)
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/entities/EmptyItem.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/entities/EmptyItem.kt
index b05c92d..2403c73 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/entities/EmptyItem.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/entities/EmptyItem.kt
@@ -1,6 +1,6 @@
package io.gripxtech.odoojsonrpcclient.core.utils.recycler.entities
-import android.support.annotation.DrawableRes
+import androidx.annotation.DrawableRes
data class EmptyItem(
val message: CharSequence,
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/entities/EmptyViewHolder.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/entities/EmptyViewHolder.kt
index 53d24a7..574f0a4 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/entities/EmptyViewHolder.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/entities/EmptyViewHolder.kt
@@ -1,6 +1,6 @@
package io.gripxtech.odoojsonrpcclient.core.utils.recycler.entities
-import android.support.v7.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView
import io.gripxtech.odoojsonrpcclient.databinding.ItemViewRecyclerEmptyBinding
class EmptyViewHolder(
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/entities/ErrorViewHolder.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/entities/ErrorViewHolder.kt
index 8d37c04..93b7924 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/entities/ErrorViewHolder.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/entities/ErrorViewHolder.kt
@@ -1,6 +1,6 @@
package io.gripxtech.odoojsonrpcclient.core.utils.recycler.entities
-import android.support.v7.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView
import io.gripxtech.odoojsonrpcclient.databinding.ItemViewRecyclerErrorBinding
class ErrorViewHolder(
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/entities/LessViewHolder.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/entities/LessViewHolder.kt
index 2c27de9..0b25273 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/entities/LessViewHolder.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/entities/LessViewHolder.kt
@@ -1,6 +1,6 @@
package io.gripxtech.odoojsonrpcclient.core.utils.recycler.entities
-import android.support.v7.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView
import io.gripxtech.odoojsonrpcclient.databinding.ItemViewRecyclerLessBinding
class LessViewHolder(
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/entities/MoreViewHolder.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/entities/MoreViewHolder.kt
index 1c8041b..a88d081 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/entities/MoreViewHolder.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/entities/MoreViewHolder.kt
@@ -1,6 +1,6 @@
package io.gripxtech.odoojsonrpcclient.core.utils.recycler.entities
-import android.support.v7.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView
import io.gripxtech.odoojsonrpcclient.databinding.ItemViewRecyclerMoreBinding
class MoreViewHolder(
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/helpers/ItemTouchHelperAdapter.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/helpers/ItemTouchHelperAdapter.kt
index 4fe575f..7271616 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/helpers/ItemTouchHelperAdapter.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/helpers/ItemTouchHelperAdapter.kt
@@ -16,7 +16,7 @@
package io.gripxtech.odoojsonrpcclient.core.utils.recycler.helpers
-import android.support.v7.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView
/**
* Interface to listen for a move or dismissal event from a {@link ItemTouchHelper.Callback}.
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/helpers/OnStartDragListener.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/helpers/OnStartDragListener.kt
index 3363bcf..d498c4e 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/helpers/OnStartDragListener.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/helpers/OnStartDragListener.kt
@@ -16,7 +16,7 @@
package io.gripxtech.odoojsonrpcclient.core.utils.recycler.helpers
-import android.support.v7.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView
/**
* Listener for manual initiation of a drag.
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/helpers/SimpleItemTouchHelperCallback.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/helpers/SimpleItemTouchHelperCallback.kt
index b322002..b0c0794 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/helpers/SimpleItemTouchHelperCallback.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/recycler/helpers/SimpleItemTouchHelperCallback.kt
@@ -16,9 +16,9 @@
package io.gripxtech.odoojsonrpcclient.core.utils.recycler.helpers
-import android.support.v7.widget.GridLayoutManager
-import android.support.v7.widget.RecyclerView
-import android.support.v7.widget.helper.ItemTouchHelper
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.ItemTouchHelper
+import androidx.recyclerview.widget.RecyclerView
/**
* An implementation of {@link ItemTouchHelper.Callback} that enables basic drag & drop and
@@ -31,19 +31,16 @@ import android.support.v7.widget.helper.ItemTouchHelper
* @author Paul Burke (ipaulpro)
*/
class SimpleItemTouchHelperCallback(
- val adapter: ItemTouchHelperAdapter,
- private val longPressDragEnabled: Boolean = false,
- private val itemViewSwipeEnabled: Boolean = false
+ val adapter: ItemTouchHelperAdapter,
+ private val longPressDragEnabled: Boolean = false,
+ private val itemViewSwipeEnabled: Boolean = false
) : ItemTouchHelper.Callback() {
companion object {
const val ALPHA_FULL = 1.0f
}
- override fun getMovementFlags(recyclerView: RecyclerView?, viewHolder: RecyclerView.ViewHolder?): Int {
- if (recyclerView == null || viewHolder == null) {
- return -1
- }
+ override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
// Set movement flags based on the layout manager
return if (recyclerView.layoutManager is GridLayoutManager) {
val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
@@ -56,10 +53,11 @@ class SimpleItemTouchHelperCallback(
}
}
- override fun onMove(recyclerView: RecyclerView?, source: RecyclerView.ViewHolder?, target: RecyclerView.ViewHolder?): Boolean {
- if (recyclerView == null || source == null || target == null) {
- return false
- }
+ override fun onMove(
+ recyclerView: RecyclerView,
+ source: RecyclerView.ViewHolder,
+ target: RecyclerView.ViewHolder
+ ): Boolean {
if (source.itemViewType != target.itemViewType) {
return false
}
@@ -69,10 +67,7 @@ class SimpleItemTouchHelperCallback(
return true
}
- override fun onSwiped(viewHolder: RecyclerView.ViewHolder?, direction: Int) {
- if (viewHolder === null) {
- return
- }
+ override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
// Notify the adapter of the dismiss
adapter.onItemDismiss(viewHolder.adapterPosition)
}
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/tabs/PagerAdapter.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/tabs/PagerAdapter.kt
index 35ce8fc..5391167 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/tabs/PagerAdapter.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/tabs/PagerAdapter.kt
@@ -1,12 +1,12 @@
package io.gripxtech.odoojsonrpcclient.core.utils.tabs
-import android.support.v4.app.Fragment
-import android.support.v4.app.FragmentManager
-import android.support.v4.app.FragmentPagerAdapter
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentManager
+import androidx.fragment.app.FragmentPagerAdapter
class PagerAdapter(
- fragmentManager: FragmentManager,
- val items: ArrayList
+ fragmentManager: FragmentManager,
+ val items: ArrayList
) : FragmentPagerAdapter(fragmentManager) {
override fun getItem(position: Int): Fragment = items[position].fragment
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/tabs/PagerItem.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/tabs/PagerItem.kt
index 2adef8d..ebc1766 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/tabs/PagerItem.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/core/utils/tabs/PagerItem.kt
@@ -1,6 +1,6 @@
package io.gripxtech.odoojsonrpcclient.core.utils.tabs
-import android.support.v4.app.Fragment
+import androidx.fragment.app.Fragment
data class PagerItem(
val title: String,
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/customer/CustomerAdapter.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/customer/CustomerAdapter.kt
index c2eebc6..910e549 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/customer/CustomerAdapter.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/customer/CustomerAdapter.kt
@@ -1,15 +1,15 @@
package io.gripxtech.odoojsonrpcclient.customer
-import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
import io.gripxtech.odoojsonrpcclient.core.utils.recycler.RecyclerBaseAdapter
import io.gripxtech.odoojsonrpcclient.customer.entities.Customer
import io.gripxtech.odoojsonrpcclient.databinding.ItemViewCustomerBinding
class CustomerAdapter(
- val fragment: CustomerFragment,
- items: ArrayList
+ private val fragment: CustomerFragment,
+ items: ArrayList
) : RecyclerBaseAdapter(items, fragment.binding.rv) {
companion object {
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/customer/CustomerFragment.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/customer/CustomerFragment.kt
index fdbe887..3323474 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/customer/CustomerFragment.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/customer/CustomerFragment.kt
@@ -2,13 +2,11 @@ package io.gripxtech.odoojsonrpcclient.customer
import android.content.res.Configuration
import android.os.Bundle
-import android.support.v4.app.Fragment
-import android.support.v7.app.ActionBarDrawerToggle
-import android.support.v7.widget.DividerItemDecoration
-import android.support.v7.widget.LinearLayoutManager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.appcompat.app.ActionBarDrawerToggle
+import androidx.recyclerview.widget.RecyclerView
import com.google.gson.reflect.TypeToken
import io.gripxtech.odoojsonrpcclient.*
import io.gripxtech.odoojsonrpcclient.core.Odoo
@@ -16,7 +14,7 @@ import io.gripxtech.odoojsonrpcclient.customer.entities.Customer
import io.gripxtech.odoojsonrpcclient.databinding.FragmentCustomerBinding
import io.reactivex.disposables.CompositeDisposable
-class CustomerFragment : Fragment() {
+class CustomerFragment : androidx.fragment.app.Fragment() {
companion object {
@@ -29,18 +27,18 @@ class CustomerFragment : Fragment() {
private const val TYPE = "type"
fun newInstance(customerType: CustomerType) =
- CustomerFragment().apply {
- arguments = Bundle().apply {
- putString(TYPE, customerType.name)
- }
+ CustomerFragment().apply {
+ arguments = Bundle().apply {
+ putString(TYPE, customerType.name)
}
+ }
}
lateinit var activity: MainActivity private set
lateinit var binding: FragmentCustomerBinding private set
- lateinit var compositeDisposable: CompositeDisposable private set
+ private var compositeDisposable: CompositeDisposable? = null
- private lateinit var customerType: CustomerType
+ private var customerType: CustomerType = CustomerType.Customer
private lateinit var drawerToggle: ActionBarDrawerToggle
val adapter: CustomerAdapter by lazy {
@@ -50,8 +48,11 @@ class CustomerFragment : Fragment() {
private val customerListType = object : TypeToken>() {}.type
private val limit = RECORD_LIMIT
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?): View? {
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ compositeDisposable?.dispose()
compositeDisposable = CompositeDisposable()
// Inflate the layout for this fragment
@@ -63,7 +64,7 @@ class CustomerFragment : Fragment() {
super.onActivityCreated(savedInstanceState)
activity = getActivity() as MainActivity
arguments?.let {
- customerType = CustomerType.valueOf(it.getString(TYPE))
+ customerType = CustomerType.valueOf(it.getString(TYPE) ?: "")
}
// Hiding MainActivity's AppBarLayout as well as NestedScrollView first
@@ -91,17 +92,22 @@ class CustomerFragment : Fragment() {
actionBar.setDisplayHomeAsUpEnabled(true)
}
- drawerToggle = ActionBarDrawerToggle(activity, activity.binding.dl,
- binding.tb, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
+ drawerToggle = ActionBarDrawerToggle(
+ activity, activity.binding.dl,
+ binding.tb, R.string.navigation_drawer_open, R.string.navigation_drawer_close
+ )
activity.binding.dl.addDrawerListener(drawerToggle)
drawerToggle.syncState()
- val layoutManager = LinearLayoutManager(
- activity, LinearLayoutManager.VERTICAL, false
+ val layoutManager = androidx.recyclerview.widget.LinearLayoutManager(
+ activity, RecyclerView.VERTICAL, false
)
binding.rv.layoutManager = layoutManager
binding.rv.addItemDecoration(
- DividerItemDecoration(activity, LinearLayoutManager.VERTICAL)
+ androidx.recyclerview.widget.DividerItemDecoration(
+ activity,
+ androidx.recyclerview.widget.LinearLayoutManager.VERTICAL
+ )
)
adapter.setupScrollListener(binding.rv)
@@ -131,7 +137,7 @@ class CustomerFragment : Fragment() {
binding.rv.adapter = adapter
}
- override fun onConfigurationChanged(newConfig: Configuration?) {
+ override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
if (::drawerToggle.isInitialized) {
drawerToggle.onConfigurationChanged(newConfig)
@@ -139,26 +145,28 @@ class CustomerFragment : Fragment() {
}
override fun onDestroyView() {
- compositeDisposable.dispose()
+ compositeDisposable?.dispose()
super.onDestroyView()
}
private fun fetchCustomer() {
- Odoo.searchRead("res.partner", Customer.fields,
- when (customerType) {
- CustomerType.Customer -> {
- listOf(listOf("customer", "=", true))
- }
- CustomerType.Supplier -> {
- listOf(listOf("supplier", "=", true))
- }
- CustomerType.Company -> {
- listOf(listOf("is_company", "=", true))
- }
- }, adapter.rowItemCount, limit, "name ASC") {
+ Odoo.searchRead(
+ "res.partner", Customer.fields,
+ when (customerType) {
+ CustomerType.Customer -> {
+ listOf(listOf("customer", "=", true))
+ }
+ CustomerType.Supplier -> {
+ listOf(listOf("supplier", "=", true))
+ }
+ CustomerType.Company -> {
+ listOf(listOf("is_company", "=", true))
+ }
+ }, adapter.rowItemCount, limit, "name ASC"
+ ) {
onSubscribe { disposable ->
- compositeDisposable.add(disposable)
+ compositeDisposable?.add(disposable)
}
onNext { response ->
@@ -182,10 +190,9 @@ class CustomerFragment : Fragment() {
}
}
adapter.addRowItems(items)
- compositeDisposable.dispose()
- compositeDisposable = CompositeDisposable()
} else {
adapter.showError(searchRead.errorMessage)
+ activity.promptReport(searchRead.odooError)
}
} else {
adapter.showError(response.errorBodySpanned)
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/customer/CustomerViewHolder.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/customer/CustomerViewHolder.kt
index 25db4b8..20b9a99 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/customer/CustomerViewHolder.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/customer/CustomerViewHolder.kt
@@ -1,6 +1,6 @@
package io.gripxtech.odoojsonrpcclient.customer
-import android.support.v7.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView
import io.gripxtech.odoojsonrpcclient.databinding.ItemViewCustomerBinding
class CustomerViewHolder(
diff --git a/app/src/main/java/io/gripxtech/odoojsonrpcclient/customer/entities/Customer.kt b/app/src/main/java/io/gripxtech/odoojsonrpcclient/customer/entities/Customer.kt
index 20fa2ce..2afd014 100644
--- a/app/src/main/java/io/gripxtech/odoojsonrpcclient/customer/entities/Customer.kt
+++ b/app/src/main/java/io/gripxtech/odoojsonrpcclient/customer/entities/Customer.kt
@@ -1,8 +1,8 @@
package io.gripxtech.odoojsonrpcclient.customer.entities
-import android.databinding.BindingAdapter
import android.util.Base64
import android.widget.ImageView
+import androidx.databinding.BindingAdapter
import com.google.gson.JsonElement
import com.google.gson.annotations.Expose
import com.google.gson.annotations.SerializedName
@@ -11,80 +11,84 @@ import io.gripxtech.odoojsonrpcclient.GlideApp
data class Customer(
- @Expose
- @SerializedName("id")
- val id: Int,
+ @Expose
+ @SerializedName("id")
+ val id: Int,
- @Expose
- @SerializedName("name")
- val name: String,
+ @Expose
+ @SerializedName("name")
+ val name: String,
- @Expose
- @SerializedName("email")
- val email: String,
+ @Expose
+ @SerializedName("email")
+ val email: String,
- @Expose
- @SerializedName("company_name")
- val companyName: String,
+ @Expose
+ @SerializedName("company_name")
+ val companyName: String,
- @Expose
- @SerializedName("image_small")
- val imageSmall: String,
+ @Expose
+ @SerializedName("image_small")
+ val imageSmall: String,
- @Expose
- @SerializedName("website")
- val website: String,
+ @Expose
+ @SerializedName("website")
+ val website: String,
- @Expose
- @SerializedName("phone")
- val phone: String,
+ @Expose
+ @SerializedName("phone")
+ val phone: String,
- @Expose
- @SerializedName("mobile")
- val mobile: String,
+ @Expose
+ @SerializedName("mobile")
+ val mobile: String,
- @Expose
- @SerializedName("full_address")
- val fullAddress: String,
+ @Expose
+ @SerializedName("full_address")
+ val fullAddress: String,
- @Expose
- @SerializedName("state_id")
- val stateId: JsonElement,
+ @Expose
+ @SerializedName("state_id")
+ val stateId: JsonElement,
- @Expose
- @SerializedName("country_id")
- val countryId: JsonElement,
+ @Expose
+ @SerializedName("country_id")
+ val countryId: JsonElement,
- @Expose
- @SerializedName("comment")
- val comment: String,
+ @Expose
+ @SerializedName("comment")
+ val comment: String,
- @Expose
- @SerializedName("is_company")
- val isCompany: Boolean
+ @Expose
+ @SerializedName("is_company")
+ val isCompany: Boolean
) {
companion object {
@JvmStatic
@BindingAdapter("image_small", "name")
fun loadImage(view: ImageView, imageSmall: String, name: String) {
GlideApp.with(view.context)
- .asBitmap()
- .load(
- if (imageSmall.isNotEmpty())
- Base64.decode(imageSmall, Base64.DEFAULT)
- else
- (view.context.applicationContext as App)
- .getLetterTile(if (name.isNotEmpty()) name else "X"))
- .into(view)
+ .asBitmap()
+ .load(
+ if (imageSmall.isNotEmpty())
+ Base64.decode(imageSmall, Base64.DEFAULT)
+ else
+ (view.context.applicationContext as App)
+ .getLetterTile(if (name.isNotEmpty()) name else "X")
+ )
+ .dontAnimate()
+ .circleCrop()
+ .into(view)
}
@JvmField
val fieldsMap: Map = mapOf(
- "id" to "id", "name" to "Name", "email" to "Email",
- "company_name" to "Company Name", "image_small" to "Image", "website" to "Website",
- "phone" to "Phone Number", "mobile" to "Mobile Number",/* "full_address" to "Full Address",*/
- "state_id" to "State", "country_id" to "Country", "comment" to "Internal Note",
- "is_company" to "Is Company")
+ "id" to "id", "name" to "Name", "email" to "Email",
+ "company_name" to "Company Name", "image_small" to "Image", "website" to "Website",
+ "phone" to "Phone Number", "mobile" to "Mobile Number",/* "full_address" to "Full Address",*/
+ "state_id" to "State", "country_id" to "Country", "comment" to "Internal Note",
+ "is_company" to "Is Company"
+ )
@JvmField
val fields: ArrayList = fieldsMap.keys.toMutableList() as ArrayList
diff --git a/app/src/main/res/drawable-hdpi/baseline_feedback_black_18.png b/app/src/main/res/drawable-hdpi/baseline_feedback_black_18.png
new file mode 100644
index 0000000..eac2e79
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_feedback_black_18.png differ
diff --git a/app/src/main/res/drawable-mdpi/baseline_feedback_black_18.png b/app/src/main/res/drawable-mdpi/baseline_feedback_black_18.png
new file mode 100644
index 0000000..21b48cd
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_feedback_black_18.png differ
diff --git a/app/src/main/res/drawable-xhdpi/baseline_feedback_black_18.png b/app/src/main/res/drawable-xhdpi/baseline_feedback_black_18.png
new file mode 100644
index 0000000..0c97c40
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_feedback_black_18.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/baseline_feedback_black_18.png b/app/src/main/res/drawable-xxhdpi/baseline_feedback_black_18.png
new file mode 100644
index 0000000..ddaa90f
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_feedback_black_18.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_feedback_black_18.png b/app/src/main/res/drawable-xxxhdpi/baseline_feedback_black_18.png
new file mode 100644
index 0000000..c1c3bba
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_feedback_black_18.png differ
diff --git a/app/src/main/res/drawable/ic_assignment_black_24dp.xml b/app/src/main/res/drawable/ic_assignment_black_24dp.xml
new file mode 100644
index 0000000..4e1c86c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_assignment_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_dashboard_white_24dp.xml b/app/src/main/res/drawable/ic_dashboard_white_24dp.xml
new file mode 100644
index 0000000..f327e1f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_dashboard_white_24dp.xml
@@ -0,0 +1,6 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_home_black_24dp.xml b/app/src/main/res/drawable/ic_home_black_24dp.xml
new file mode 100644
index 0000000..de832bb
--- /dev/null
+++ b/app/src/main/res/drawable/ic_home_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..867f14a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_person_add_black_24dp.xml b/app/src/main/res/drawable/ic_person_add_black_24dp.xml
new file mode 100644
index 0000000..53baa5b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_person_add_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
index fe049ba..cf9936e 100644
--- a/app/src/main/res/layout/activity_login.xml
+++ b/app/src/main/res/layout/activity_login.xml
@@ -1,196 +1,196 @@
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools">
-
-
-
+ android:layout_height="match_parent"
+ android:background="#f1f1f1"
+ android:clipToPadding="false"
+ android:paddingBottom="@dimen/_64sdp"
+ tools:context=".core.authenticator.LoginActivity">
+
+