feat: Codigo fuente SIO Mobile - App Android para Operadores

Aplicacion movil Android para el sistema SIO (Sistema Integral de Operaciones)
de Drenax. Permite a los operadores gestionar sus servicios diarios.

Funcionalidades principales:
- Login y autenticacion JWT
- Checklist de vehiculos
- Gestion de jornada laboral
- Lista de servicios asignados
- Captura de evidencias fotograficas
- Firma digital del cliente
- Encuestas de satisfaccion
- Notificaciones push (Firebase)
- Almacenamiento offline (ObjectBox)
- Geolocalizacion y mapas

Stack tecnologico:
- Kotlin
- Android SDK 33
- Retrofit + OkHttp
- ObjectBox
- Firebase (FCM, Crashlytics, Analytics)
- Google Maps

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
SIO Admin
2026-01-18 03:09:03 +00:00
commit 8c4294e67b
228 changed files with 20912 additions and 0 deletions

1
app/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

109
app/build.gradle Normal file
View File

@@ -0,0 +1,109 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
}
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'
android {
compileSdkVersion 33
defaultConfig {
applicationId "com.iesoluciones.siodrenax"
minSdkVersion 28
targetSdkVersion 33
versionCode 3
//versionName "0.48"
versionName "1.00"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true // To accept variables colors in vectors
}
buildTypes {
debug {
// -------- API --------
buildConfigField("String", "BASE_URL", '"https://sio-api.consultoria-as.com/api/"') //PROD - Cloudflare Tunnel
buildConfigField("String", "STORAGE_URL", '"https://sio-api.consultoria-as.com/storage/"') //PROD STORAGE
//buildConfigField("String", "BASE_URL", '"http://107.170.231.250/v1/api/"') //PROD OLD
//buildConfigField("String", "STORAGE_URL", '"http://107.170.231.250/storage/"') //PROD STORAGE OLD
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
// -------- API --------
buildConfigField("String", "BASE_URL", '"https://sio-api.consultoria-as.com/api/"') //PROD - Cloudflare Tunnel
buildConfigField("String", "STORAGE_URL", '"https://sio-api.consultoria-as.com/storage/"') //PROD STORAGE
//buildConfigField("String", "BASE_URL", '"http://107.170.231.250/v1/api/"') //PROD OLD
//buildConfigField("String", "STORAGE_URL", '"http://107.170.231.250/storage/"') //PROD STORAGE OLD
}
}
buildFeatures {
viewBinding true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.work:work-runtime:2.8.0'
implementation 'com.google.android.material:material:1.5.0'
implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' // Android Jetpack
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1' //Android Jetpack
implementation "android.arch.work:work-runtime:1.0.1" //Android Jetpack //Android Jetpack
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' //RxAndroid
implementation 'io.reactivex.rxjava2:rxjava:2.2.2' //RxJava
implementation 'com.squareup.retrofit2:converter-gson:2.4.0' //GSON Converter for Retrofit
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0' //Rx Requests for Retrofit
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1' //Logging interceptor for every Request
implementation 'com.github.bumptech.glide:glide:4.11.0'
kapt 'com.github.bumptech.glide:compiler:4.11.0'
implementation 'com.google.android.gms:play-services-maps:18.0.2'// Google Maps Android API
// ----- ObjectBox Data Browser -----
debugImplementation "io.objectbox:objectbox-android-objectbrowser:$objectboxVersion"
releaseImplementation "io.objectbox:objectbox-android:$objectboxVersion"
implementation "io.objectbox:objectbox-kotlin:$objectboxVersion"
// ----- Testing -----
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test:runner:1.4.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
// ----- Firebase -----
implementation 'com.google.firebase:firebase-messaging:22.0.0'
implementation 'com.google.firebase:firebase-crashlytics:18.2.0'
implementation 'com.google.firebase:firebase-core:19.0.0'
implementation platform('com.google.firebase:firebase-bom:28.3.1')
implementation 'com.google.firebase:firebase-crashlytics-ktx'
implementation 'com.google.firebase:firebase-analytics-ktx'
// ----- Camera & Sign -----
implementation 'io.fotoapparat:fotoapparat:2.7.0'
implementation 'com.github.gcacace:signature-pad:1.3.1'
// ----- HTML TO PDF -----
implementation 'io.github.nvest-solutions:html-to-pdf-convertor:1.0.0'
implementation 'com.github.naya-aastra:SkewPdfView:1.1'
implementation project(':mylibrary')
}
apply plugin: 'io.objectbox' // Apply last.

48
app/google-services.json Normal file
View File

@@ -0,0 +1,48 @@
{
"project_info": {
"project_number": "108152813393",
"firebase_url": "https://drenaxx-f561e.firebaseio.com",
"project_id": "drenaxx-f561e",
"storage_bucket": "drenaxx-f561e.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:108152813393:android:6a2ce608fab88e98",
"android_client_info": {
"package_name": "com.iesoluciones.siodrenax"
}
},
"oauth_client": [
{
"client_id": "108152813393-sui8jok0le3f873uoceguvrss61r8e8s.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.iesoluciones.siodrenax",
"certificate_hash": "43d5f941fa659b3f3e874fce11ff528d5ce26f1e"
}
},
{
"client_id": "108152813393-g09ea87ufqm21om9qt2v45fgl079d704.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyB05HH7lJlT0AIlILWlit467ArJ11W5vTc"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "108152813393-g09ea87ufqm21om9qt2v45fgl079d704.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
}
],
"configuration_version": "1"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

21
app/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,24 @@
package com.iesoluciones.siodrenax
import androidx.test.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getTargetContext()
assertEquals("com.iesoluciones.siodrenax", appContext.packageName)
}
}

View File

@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.iesoluciones.siodrenax">
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
tools:ignore="ProtectedPermissions" />
<uses-feature
android:glEsVersion="0x00020000"
android:required="true" />
<uses-feature
android:name="android.hardware.telephony"
android:required="false" />
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:name=".App"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true">
<activity
android:name=".activities.PdfViewerActivity"
android:screenOrientation="portrait"
android:exported="true" />
<activity android:name=".activities.OrdersManagerActivity" />
<activity
android:name=".activities.CameraActivity"
android:screenOrientation="landscape" />
<activity android:name=".activities.NextServiceActivity" />
<activity android:name=".activities.ConfirmationActivity" />
<activity
android:name=".activities.SignatureActivity"
android:screenOrientation="sensorLandscape" />
<activity
android:name=".activities.SurveyActivity"
android:screenOrientation="portrait" />
<activity android:name=".activities.OrderProgressActivity" />
<activity
android:name=".activities.OrderDetailActivity"
android:screenOrientation="portrait" />
<activity android:name=".activities.OperatorsActivity" />
<activity android:name=".activities.WorkdayManagerActivity" />
<activity
android:name=".activities.OrdersActivity"
android:launchMode="singleTask" />
<activity android:name=".activities.WorkdayActivity" />
<activity android:name=".activities.HerramientaSurveyActivity" />
<activity android:name=".activities.MaterialSurveyActivity" />
<activity android:name=".activities.RevisionSurveyActivity" />
<activity
android:name=".activities.LoginActivity"
android:screenOrientation="portrait"
android:theme="@style/NoPreview"
android:windowSoftInputMode="adjustPan" />
<activity
android:name=".activities.SplashActivity"
android:exported="true"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyB4TQiB2JsblDI2hvrWk__2psT3OzIPErc" /> <!-- Services -->
<service
android:name=".services.NotificationService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

View File

@@ -0,0 +1,116 @@
package com.iesoluciones.siodrenax
import android.annotation.SuppressLint
import android.app.Application
import android.content.Context
import android.util.Log
import com.iesoluciones.siodrenax.BuildConfig.BASE_URL
import com.iesoluciones.siodrenax.entities.*
import com.iesoluciones.siodrenax.network.Api
import com.iesoluciones.siodrenax.network.TokenInterceptor
import io.objectbox.Box
import io.objectbox.BoxStore
import io.objectbox.android.AndroidObjectBrowser
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
val preferencesHelper: PreferencesHelper by lazy { App.prefs!! }
val api: Api by lazy { App.api!! }
val userBox: Box<User> by lazy { App.userBox!! }
val checkListQuestionBox: Box<CheckListQuestion> by lazy { App.checkListQuestionBox!! }
val vehicleBox: Box<Vehicle> by lazy { App.vehicleBox!! }
val orderBox: Box<Order> by lazy { App.orderBox!! }
val orderProgressBox: Box<OrderProgress> by lazy { App.orderProgressBox!! }
val evidenceBox: Box<Evidence> by lazy { App.evidenceBox!! }
val businessQuestionBox: Box<BusinessQuestion> by lazy { App.businessQuestionBox!! }
val domesticQuestionBox: Box<DomesticQuestion> by lazy { App.domesticQuestionBox!! }
val negativeServiceReasonBox: Box<NegativeServiceReason> by lazy { App.negativeServiceReasonBox!! }
val businessAnswerBox: Box<BusinessAnswer> by lazy { App.businessAnswerBox!! }
val domesticAnswerBox: Box<DomesticAnswer> by lazy { App.domesticAnswerBox!! }
val savedAnswerBox: Box<SavedAnswer> by lazy { App.savedAnswerBox!! }
val operatorBox: Box<Operator> by lazy {App.operatorBox!!}
val nextDayOrderBox: Box<NextDayOrder> by lazy {App.nextDayOrderBox!!}
class App : Application() {
companion object {
var prefs: PreferencesHelper? = null
var api: Api? = null
var context: Context? = null
var shareInstance: App? = null
var boxStore: BoxStore? = null
var userBox: Box<User>? = null
var checkListQuestionBox: Box<CheckListQuestion>? = null
var vehicleBox: Box<Vehicle>? = null
var orderBox: Box<Order>? = null
var orderProgressBox: Box<OrderProgress>? = null
var evidenceBox: Box<Evidence>? = null
var businessQuestionBox: Box<BusinessQuestion>? = null
var domesticQuestionBox: Box<DomesticQuestion>? = null
var negativeServiceReasonBox: Box<NegativeServiceReason>? = null
var businessAnswerBox: Box<BusinessAnswer>? = null
var domesticAnswerBox: Box<DomesticAnswer>? = null
var savedAnswerBox: Box<SavedAnswer>? = null
var operatorBox: Box<Operator>? = null
var nextDayOrderBox: Box<NextDayOrder>? = null
}
@SuppressLint("CheckResult")
override fun onCreate() {
super.onCreate()
prefs = PreferencesHelper(applicationContext)
shareInstance = this
context = this
boxStore = MyObjectBox.builder().androidContext(this).build()
userBox = boxStore!!.boxFor(User::class.java)
checkListQuestionBox = boxStore!!.boxFor(CheckListQuestion::class.java)
vehicleBox = boxStore!!.boxFor(Vehicle::class.java)
orderBox = boxStore!!.boxFor(Order::class.java)
orderProgressBox = boxStore!!.boxFor(OrderProgress::class.java)
evidenceBox = boxStore!!.boxFor(Evidence::class.java)
businessQuestionBox = boxStore!!.boxFor(BusinessQuestion::class.java)
domesticQuestionBox = boxStore!!.boxFor(DomesticQuestion::class.java)
negativeServiceReasonBox = boxStore!!.boxFor(NegativeServiceReason::class.java)
businessAnswerBox = boxStore!!.boxFor(BusinessAnswer::class.java)
domesticAnswerBox = boxStore!!.boxFor(DomesticAnswer::class.java)
savedAnswerBox = boxStore!!.boxFor(SavedAnswer::class.java)
operatorBox = boxStore!!.boxFor(Operator::class.java)
nextDayOrderBox = boxStore!!.boxFor(NextDayOrder::class.java)
//Starting ObjectBox Data Browser (ONLY FOR TEST!!!)
// TODO Comentar antes de liberar
if (BuildConfig.DEBUG) {
val started = AndroidObjectBrowser(boxStore).start(this)
Log.i("ObjectBrowser", "Started: $started")
}
//Instantiate Logging interceptor
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BASIC
//Build HTTP Client
val client = OkHttpClient.Builder()
.addInterceptor(TokenInterceptor())
.addInterceptor(logging)
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.build()
//Build Retrofit Client
val retrofit = Retrofit.Builder()
.client(client)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build()
api = retrofit.create(Api::class.java)
}
}

View File

@@ -0,0 +1,65 @@
package com.iesoluciones.siodrenax
import android.content.Context
class PreferencesHelper(context: Context) {
private val fileName = "share_preferences_file"
private val prefs = context.getSharedPreferences(fileName, Context.MODE_PRIVATE)
private val TOKEN_API = "tokenApi"
private val TOKEN_FIREBASE = "tokenFirebase"
private val IS_MANAGER = "isManager"
private val CHECK_LIST_PROGRESS = "checkListProgress"
private val IS_ENABLE_HERRAMIENTA_SURVEY = "isEnableHerramientaSurvey"
private val WORKDAY_STARTED = "workday_started"
private val WORKDAY_ID = "workday_id"
private val GPS_INTERVAL_IN_MINUTES = "gps_interval"
private val ORDER_PROGRESS = "order_in_progress"
private val IS_ENABLE_TO_SYNC = "syncEnable"
var tokenApi: String?
get() = prefs.getString(TOKEN_API, null)
set(value) = prefs.edit().putString(TOKEN_API, value).apply()
var tokenFirebase: String?
get() = prefs.getString(TOKEN_FIREBASE, "default_token_firebase")
set(value) = prefs.edit().putString(TOKEN_FIREBASE, value).apply()
var isManager: Boolean
get() = prefs.getBoolean(IS_MANAGER, false)
set(value) = prefs.edit().putBoolean(IS_MANAGER, value).apply()
var checkListProgress: String?
get() = prefs.getString(CHECK_LIST_PROGRESS, null)
set(value) = prefs.edit().putString(CHECK_LIST_PROGRESS, value).apply()
var isEnableHerramientaSurvey: Boolean
get() = prefs.getBoolean(IS_ENABLE_HERRAMIENTA_SURVEY, true)
set(value) = prefs.edit().putBoolean(IS_ENABLE_HERRAMIENTA_SURVEY, value).apply()
var workdayStarted: Boolean
get() = prefs.getBoolean(WORKDAY_STARTED, false)
set(value) = prefs.edit().putBoolean(WORKDAY_STARTED, value).apply()
var workdayId: Int
get() = prefs.getInt(WORKDAY_ID, 0)
set(value) = prefs.edit().putInt(WORKDAY_ID, value).apply()
var gpsInterval: Int
get() = prefs.getInt(GPS_INTERVAL_IN_MINUTES, 3)
set(value) = prefs.edit().putInt(GPS_INTERVAL_IN_MINUTES, value).apply()
var orderInProgress: Long?
get() = prefs.getLong(ORDER_PROGRESS, 0L)
set(value){
if (value == null)
prefs.edit().remove(ORDER_PROGRESS).apply()
else
prefs.edit().putLong(ORDER_PROGRESS, value).apply()
}
var isEnableToSync: Boolean
get() = prefs.getBoolean(IS_ENABLE_TO_SYNC, true)
set(value) = prefs.edit().putBoolean(IS_ENABLE_TO_SYNC, value).apply()
}

View File

@@ -0,0 +1,262 @@
package com.iesoluciones.siodrenax.activities
import android.animation.Animator
import android.graphics.Bitmap
import android.os.Bundle
import android.view.View
import android.view.animation.DecelerateInterpolator
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.databinding.ActivityCameraBinding
import com.iesoluciones.siodrenax.entities.Evidence
import com.iesoluciones.siodrenax.repositories.EvidenceRepository
import com.iesoluciones.siodrenax.utils.Constants.Companion.DEFAULT_LATITUDE
import com.iesoluciones.siodrenax.utils.Constants.Companion.DEFAULT_LONGITUDE
import com.iesoluciones.siodrenax.utils.Constants.Companion.EVIDENCE_ID
import com.iesoluciones.siodrenax.utils.Constants.Companion.EVIDENCIA
import com.iesoluciones.siodrenax.utils.Constants.Companion.HIDE_ALPHA
import com.iesoluciones.siodrenax.utils.Constants.Companion.HIDE_DURATION
import com.iesoluciones.siodrenax.utils.Constants.Companion.SHOW_ALPHA
import com.iesoluciones.siodrenax.utils.Constants.Companion.SHOW_DURATION
import com.iesoluciones.siodrenax.utils.Constants.Companion.SHOW_TITLE_ALPHA
import com.iesoluciones.siodrenax.utils.Constants.Companion.SHOW_TITLE_DELAY
import com.iesoluciones.siodrenax.utils.Constants.Companion.SHOW_TITLE_DURATION
import com.iesoluciones.siodrenax.utils.HelperUtil
import com.iesoluciones.siodrenax.utils.toastLong
import com.iesoluciones.siodrenax.viewmodels.EvidenceViewModel
import io.fotoapparat.Fotoapparat
import io.fotoapparat.configuration.UpdateConfiguration
import io.fotoapparat.error.CameraErrorListener
import io.fotoapparat.exception.camera.CameraException
import io.fotoapparat.log.fileLogger
import io.fotoapparat.log.logcat
import io.fotoapparat.log.loggers
import io.fotoapparat.parameter.ScaleType
import io.fotoapparat.result.BitmapPhoto
import io.fotoapparat.result.WhenDoneListener
import io.fotoapparat.selector.back
import io.fotoapparat.selector.off
import io.fotoapparat.selector.torch
class CameraActivity : AppCompatActivity() {
private var resultado: Int = RESULT_OK
private var torchOn = false
private var show = true
private lateinit var binding: ActivityCameraBinding
private lateinit var evidenceViewModel: EvidenceViewModel
private lateinit var evidenceRepository: EvidenceRepository
private lateinit var evidence: Evidence
private lateinit var fotoapparat: Fotoapparat
private var bitmapResult: Bitmap? = null
private lateinit var animListenerHide: Animator.AnimatorListener
private lateinit var animListenerShow: Animator.AnimatorListener
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityCameraBinding.inflate(layoutInflater)
setContentView(binding.root)
evidenceViewModel = ViewModelProvider(this)[EvidenceViewModel::class.java]
evidenceRepository = EvidenceRepository()
binding.cameraView.visibility = View.VISIBLE
evidence = evidenceRepository.getEvidenceById(intent.getLongExtra(EVIDENCE_ID, 0L))
showTitle(EVIDENCIA + evidence.getTypeDescription() + " " + evidence.evidenceNo)
fotoapparat = createFotoapparat()
binding.capture.setOnClickListener { takePicture() }
binding.ivTorch.setImageResource(R.drawable.torch_off)
binding.ivTorch.setOnClickListener { toggleTorchOnImageView() }
binding.buttonConfirm.setOnClickListener { confirm() }
binding.buttonCancel.setOnClickListener { cancel() }
binding.tvCerrar.setOnClickListener {
fotoapparat.stop()
finish()
}
prepareGuidelines()
attachObservers()
}
override fun onStart() {
super.onStart()
fotoapparat.start()
}
override fun onStop() {
super.onStop()
fotoapparat.stop()
finish()
}
override fun finish() {
setResult(resultado)
super.finish()
}
private fun createFotoapparat(): Fotoapparat {
return Fotoapparat.with(this)
.into(binding.cameraView)
.previewScaleType(ScaleType.CenterInside)
.lensPosition(back())
.logger(loggers(logcat(), fileLogger(this)))
.cameraErrorCallback(object : CameraErrorListener {
override fun onError(e: CameraException) {
toastLong(e.toString())
}
})
.build()
}
private fun toggleTorchOnImageView() {
torchOn = !torchOn
binding.ivTorch.setImageResource(if (torchOn) R.drawable.torch else R.drawable.torch_off)
fotoapparat.updateConfiguration(
UpdateConfiguration.builder()
.flash(if (torchOn) torch() else off())
.build()
)
}
private fun takePicture() {
binding.capture.isEnabled = false
binding.frameLoading.visibility = View.VISIBLE
val photoResult = fotoapparat.takePicture()
photoResult
.toBitmap()
.whenDone(object : WhenDoneListener<BitmapPhoto?> {
override fun whenDone(it: BitmapPhoto?) {
runOnUiThread {
if (it != null) {
binding.frameContent.visibility = View.VISIBLE
binding.tvDescription.text = getString(
R.string.evidence_description,
EVIDENCIA,
evidence.getTypeDescription(),
evidence.evidenceNo
)
bitmapResult = HelperUtil().scaleBitmap(it.bitmap, binding.root.context.resources.displayMetrics.widthPixels)
binding.touchImageView.setImageBitmap(bitmapResult)
binding.frameLoading.visibility = View.GONE
binding.capture.isEnabled = true
}
}
}
})
}
private fun prepareGuidelines() {
animListenerHide = object : Animator.AnimatorListener {
override fun onAnimationStart(animator: Animator) {}
override fun onAnimationEnd(animator: Animator) {
binding.tvCerrar.visibility = View.GONE
binding.relativeFullScreen.visibility = View.GONE
}
override fun onAnimationCancel(animator: Animator) {}
override fun onAnimationRepeat(animator: Animator) {}
}
animListenerShow = object : Animator.AnimatorListener {
override fun onAnimationStart(animator: Animator) {
binding.tvCerrar.visibility = View.VISIBLE
binding.relativeFullScreen.visibility = View.VISIBLE
}
override fun onAnimationEnd(animator: Animator) {}
override fun onAnimationCancel(animator: Animator) {}
override fun onAnimationRepeat(animator: Animator) {}
}
binding.touchImageView.setOnClickListener {
show = !show
if (show)
show()
else
hide()
}
}
private fun hide() {
binding.relativeFullScreen.animate()
.alpha(HIDE_ALPHA)
.setDuration(HIDE_DURATION)
.setInterpolator(DecelerateInterpolator())
.setListener(animListenerHide)
.start()
binding.tvCerrar.animate()
.alpha(HIDE_ALPHA)
.setDuration(HIDE_DURATION)
.setInterpolator(DecelerateInterpolator())
.setListener(animListenerHide)
.start()
}
private fun show() {
binding.relativeFullScreen.animate()
.alpha(SHOW_ALPHA)
.setDuration(SHOW_DURATION)
.setInterpolator(DecelerateInterpolator())
.setListener(animListenerShow)
.start()
binding.tvCerrar.animate()
.alpha(SHOW_ALPHA)
.setDuration(SHOW_DURATION)
.setInterpolator(DecelerateInterpolator())
.setListener(animListenerShow)
.start()
}
private fun showTitle(title: String?) {
binding.cardTitle.visibility = View.VISIBLE
binding.tvTitle.text = title
binding.cardTitle.alpha = SHOW_ALPHA
binding.cardTitle.animate()
.alpha(SHOW_TITLE_ALPHA)
.setDuration(SHOW_TITLE_DURATION)
.setInterpolator(DecelerateInterpolator())
.setStartDelay(SHOW_TITLE_DELAY)
.start()
}
private fun confirm() {
if (bitmapResult != null) {
binding.buttonConfirm.visibility = View.GONE
binding.buttonCancel.visibility = View.GONE
binding.buttonConfirm.isEnabled = false
evidenceViewModel.saveImage(filesDir, bitmapResult!!)
}
}
private fun cancel() {
bitmapResult = null
binding.touchImageView.setImageBitmap(null)
binding.frameContent.visibility = View.GONE
}
private fun attachObservers() {
evidenceViewModel.onImageSaved.observe(this) { evidenceSignatureLocal ->
binding.buttonConfirm.isEnabled = true
if (evidenceSignatureLocal.path != null && evidenceSignatureLocal.name != null) {
evidence.path = evidenceSignatureLocal.path
evidence.name = evidenceSignatureLocal.name
evidence.lat = DEFAULT_LATITUDE.toString()
evidence.lng = DEFAULT_LONGITUDE.toString()
evidenceRepository.saveEvidence(evidence)
} else {
resultado = RESULT_CANCELED
}
finish()
}
}
}

View File

@@ -0,0 +1,157 @@
package com.iesoluciones.siodrenax.activities
import android.animation.Animator
import android.graphics.Bitmap
import android.os.Bundle
import android.view.MenuItem
import android.view.View
import android.view.animation.DecelerateInterpolator
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.databinding.ActivityConfirmationBinding
import com.iesoluciones.siodrenax.entities.Evidence
import com.iesoluciones.siodrenax.repositories.EvidenceRepository
import com.iesoluciones.siodrenax.utils.Constants.Companion.EVIDENCE_ID
import com.iesoluciones.siodrenax.utils.Constants.Companion.EVIDENCIA
import com.iesoluciones.siodrenax.utils.Constants.Companion.HIDE_ALPHA
import com.iesoluciones.siodrenax.utils.Constants.Companion.HIDE_DURATION
import com.iesoluciones.siodrenax.utils.Constants.Companion.SHOW_ALPHA
import com.iesoluciones.siodrenax.utils.Constants.Companion.SHOW_DURATION
import com.iesoluciones.siodrenax.utils.HelperUtil
import com.iesoluciones.siodrenax.utils.TouchImageView
import com.iesoluciones.siodrenax.utils.toastLong
class ConfirmationActivity : AppCompatActivity(), View.OnClickListener {
private var show = true
private var bitmap: Bitmap? = null
private lateinit var evidence: Evidence
private lateinit var evidenceRepository: EvidenceRepository
private lateinit var animListenerHide: Animator.AnimatorListener
private lateinit var animListenerShow: Animator.AnimatorListener
private lateinit var binding: ActivityConfirmationBinding
private lateinit var tvDescription: TextView
private lateinit var tvCerrar: TextView
private lateinit var touchImageView: TouchImageView
private lateinit var relativeFullScreen: RelativeLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//window.setDecorFitsSystemWindows(false)
binding = ActivityConfirmationBinding.inflate(layoutInflater)
setContentView(binding.root)
viewBinding()
evidenceRepository = EvidenceRepository()
evidence = evidenceRepository.getEvidenceById(
intent.getLongExtra(EVIDENCE_ID, 0L)
)
try {
bitmap = HelperUtil().decodeFromFile(evidence.path!!, 0)
touchImageView.drawingCacheQuality = View.DRAWING_CACHE_QUALITY_HIGH
touchImageView.setImageBitmap(bitmap)
} catch (outOfMemoryError: OutOfMemoryError) {
toastLong(resources.getString(R.string.out_of_memory_error))
}
animListenerHide = object : Animator.AnimatorListener {
override fun onAnimationStart(animator: Animator) {}
override fun onAnimationEnd(animator: Animator) {
tvCerrar.visibility = View.GONE
relativeFullScreen.visibility = View.GONE
}
override fun onAnimationCancel(animator: Animator) {}
override fun onAnimationRepeat(animator: Animator) {}
}
animListenerShow = object : Animator.AnimatorListener {
override fun onAnimationStart(animator: Animator) {
tvCerrar.visibility = View.VISIBLE
relativeFullScreen.visibility = View.VISIBLE
}
override fun onAnimationEnd(animator: Animator) {}
override fun onAnimationCancel(animator: Animator) {}
override fun onAnimationRepeat(animator: Animator) {}
}
tvCerrar.setOnClickListener(this)
tvCerrar.visibility = View.VISIBLE
val description = EVIDENCIA + evidence.getTypeDescription() + " " + evidence.evidenceNo
tvDescription.text = description
touchImageView.setOnClickListener {
show = !show
if (show) show() else hide()
}
}
override fun onDestroy() {
super.onDestroy()
bitmap?.recycle()
}
override fun onClick(v: View?) {
if (v != null) {
when (v.id) {
R.id.tvCerrar -> onBackPressed()
}
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
android.R.id.home -> {
onBackPressed()
true
}
else -> super.onOptionsItemSelected(item)
}
}
private fun hide() {
relativeFullScreen.animate()
.alpha(HIDE_ALPHA)
.setDuration(HIDE_DURATION)
.setInterpolator(DecelerateInterpolator())
.setListener(animListenerHide)
.start()
tvCerrar.animate()
.alpha(HIDE_ALPHA)
.setDuration(HIDE_DURATION)
.setInterpolator(DecelerateInterpolator())
.setListener(animListenerHide)
.start()
}
private fun show() {
relativeFullScreen.animate()
.alpha(SHOW_ALPHA)
.setDuration(SHOW_DURATION)
.setInterpolator(DecelerateInterpolator())
.setListener(animListenerShow)
.start()
tvCerrar.animate()
.alpha(SHOW_ALPHA)
.setDuration(SHOW_DURATION)
.setInterpolator(DecelerateInterpolator())
.setListener(animListenerShow)
.start()
}
private fun viewBinding(){
tvDescription = binding.tvDescription
tvCerrar = binding.tvCerrar
touchImageView = binding.touchImageView
relativeFullScreen = binding.relativeFullScreen
}
}

View File

@@ -0,0 +1,112 @@
package com.iesoluciones.siodrenax.activities
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.iesoluciones.mylibrary.activities.ToolbarActivity
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.adapters.HerramientaSurveyAdapter
import com.iesoluciones.siodrenax.databinding.ActivityHerramientaSurveyBinding
import com.iesoluciones.siodrenax.entities.CheckListQuestion
import com.iesoluciones.siodrenax.preferencesHelper
import com.iesoluciones.siodrenax.repositories.CheckListRepository
import com.iesoluciones.siodrenax.utils.Constants
import com.iesoluciones.siodrenax.utils.HelperUtil
import com.iesoluciones.siodrenax.utils.ListPaddingDecoration
import com.iesoluciones.siodrenax.viewmodels.CheckListViewModel
class HerramientaSurveyActivity : ToolbarActivity() {
private lateinit var checkListRepository: CheckListRepository
private lateinit var checkListQuestion: List<CheckListQuestion>
private lateinit var adapter: HerramientaSurveyAdapter
private lateinit var binding: ActivityHerramientaSurveyBinding
private lateinit var checkListViewModel: CheckListViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHerramientaSurveyBinding.inflate(layoutInflater)
setContentView(binding.root)
@Suppress("USELESS_CAST")
val toolbar = binding.toolbar as Toolbar
toolbar.title = getString(R.string.herramienta)
toolbarToLoad(toolbar)
checkListRepository = CheckListRepository()
checkListQuestion = checkListRepository.getSurveyCheckList(Constants.HERRAMIENTA)
val recyclerView = binding.recycler
recyclerView.visibility = View.VISIBLE
recyclerView.addItemDecoration(ListPaddingDecoration(this@HerramientaSurveyActivity))
adapter = HerramientaSurveyAdapter(checkListQuestion)
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this@HerramientaSurveyActivity)
checkListViewModel = ViewModelProvider(this)[CheckListViewModel::class.java]
attachObservers()
binding.btnSiguiente.text = getString(R.string.hold_to_finish)
binding.btnSiguiente.setOnClickListener { finalizarEncuesta() }
}
private fun finalizarEncuesta() {
AlertDialog.Builder(this@HerramientaSurveyActivity)
.setTitle(R.string.titleDialogEncuestaOperadorFinalizar)
.setMessage(R.string.messageDialogEncuestaOperadorFinalizar)
.setPositiveButton(R.string.accept) { _, _ -> checkListViewModel.checkList() }
.setNegativeButton(R.string.cancel, null)
.show()
}
private fun attachObservers() {
checkListViewModel.isLoading.observe(this) { s ->
if (s != null) {
binding.btnSiguiente.visibility = View.GONE
binding.frameLoading.visibility = View.VISIBLE
binding.tvLoading.text = s
} else {
binding.btnSiguiente.isEnabled = true
binding.frameLoading.visibility = View.GONE
}
}
checkListViewModel.checkListSuccess.observe(this) { checkResponse ->
if (checkResponse != null) {
preferencesHelper.checkListProgress = Constants.WORKDAY
val intent = Intent(this@HerramientaSurveyActivity, WorkdayActivity::class.java)
startActivity(intent)
finish()
}
}
checkListViewModel.checkListFailure.observe(this) { throwable ->
binding.btnSiguiente.visibility = View.VISIBLE
if (throwable != null) {
disableSurvey()
HelperUtil().parseError(this, throwable)
checkListViewModel.checkListFailure.postValue(null)
}
}
}
private fun disableSurvey() {
if (!preferencesHelper.isEnableHerramientaSurvey)
return
preferencesHelper.isEnableHerramientaSurvey = false
val count = checkListQuestion.count()
for (position in 0 until count) {
adapter.notifyItemChanged(position)
}
}
}

View File

@@ -0,0 +1,119 @@
package com.iesoluciones.siodrenax.activities
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.provider.Settings
import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import com.google.android.gms.tasks.Task
import com.google.firebase.messaging.FirebaseMessaging
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.databinding.ActivityLoginBinding
import com.iesoluciones.siodrenax.preferencesHelper
import com.iesoluciones.siodrenax.repositories.CheckListRepository
import com.iesoluciones.siodrenax.utils.*
import com.iesoluciones.siodrenax.viewmodels.LoginViewModel
import java.io.File
class LoginActivity : AppCompatActivity() {
private lateinit var binding: ActivityLoginBinding
private lateinit var loginViewModel: LoginViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityLoginBinding.inflate(layoutInflater)
setContentView(binding.root)
loginViewModel = ViewModelProvider(this)[LoginViewModel::class.java]
attachObservers()
binding.btnLogin.setOnClickListener { login() }
//binding.etEmail.setText("operador@mail.com")
//binding.etPassword.setText("secret")
firebaseNotifications()
deleteSketchFolder()
}
@SuppressLint("HardwareIds")
private fun login() {
val email = binding.etEmail.text.toString()
val password = binding.etPassword.text.toString()
if (email.isNotEmpty() && password.isNotEmpty()) {
val deviceId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
loginViewModel.login(email, password, deviceId)
} else if (email.isEmpty()) {
toast(getString(R.string.required_email))
} else if (password.isEmpty()) {
toast(getString(R.string.required_password))
}
}
private fun attachObservers() {
loginViewModel.isLoading.observe(this) { s ->
if (s != null) {
binding.btnLogin.isEnabled = false
binding.frameLoading.visibility = View.VISIBLE
binding.tvLoading.text = s
} else {
binding.btnLogin.isEnabled = true
binding.frameLoading.visibility = View.GONE
}
}
loginViewModel.loginSuccess.observe(this) { success ->
if (success) {
if (preferencesHelper.isManager) {
goToActivity<WorkdayManagerActivity> {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
finish()
} else if (!preferencesHelper.isManager) {
var progressActivity: Class<*> = WorkdayActivity::class.java
if (CheckListRepository().isCheckListDone())
progressActivity = RevisionSurveyActivity::class.java
val intent = Intent(this, progressActivity)
startActivity(intent)
finish()
}
}
}
loginViewModel.loginFailure.observe(this) { throwable ->
if (throwable != null) {
HelperUtil().parseError(this, throwable)
loginViewModel.loginFailure.postValue(null)
}
}
}
private fun firebaseNotifications() {
FirebaseMessaging.getInstance().token.addOnCompleteListener { task: Task<String?> ->
if (!task.isSuccessful) {
Log.w("FIREBASE", getString(R.string.firebase_token_registration_failed), task.exception)
return@addOnCompleteListener
}
// Get new FCM registration token
preferencesHelper.tokenFirebase = task.result!!
}
}
private fun deleteSketchFolder(){
try {
File("$filesDir","pdf_sketchs").deleteRecursively()
} catch (e: Exception) {
e.printStackTrace()
}
}
}

View File

@@ -0,0 +1,61 @@
package com.iesoluciones.siodrenax.activities
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar
import androidx.recyclerview.widget.LinearLayoutManager
import com.iesoluciones.mylibrary.activities.ToolbarActivity
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.adapters.MaterialSurveyAdapter
import com.iesoluciones.siodrenax.databinding.ActivityMaterialSurveyBinding
import com.iesoluciones.siodrenax.entities.CheckListQuestion
import com.iesoluciones.siodrenax.preferencesHelper
import com.iesoluciones.siodrenax.repositories.CheckListRepository
import com.iesoluciones.siodrenax.utils.Constants
import com.iesoluciones.siodrenax.utils.ListPaddingDecoration
class MaterialSurveyActivity : ToolbarActivity() {
private lateinit var checkListRepository: CheckListRepository
private lateinit var checkListQuestion: List<CheckListQuestion>
lateinit var binding: ActivityMaterialSurveyBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMaterialSurveyBinding.inflate(layoutInflater)
setContentView(binding.root)
@Suppress("USELESS_CAST")
val toolbar = binding.toolbar as Toolbar
toolbar.title = getString(R.string.material)
toolbarToLoad(toolbar)
checkListRepository = CheckListRepository()
checkListQuestion = checkListRepository.getSurveyCheckList(Constants.MATERIAL)
val recyclerView = binding.recycler
recyclerView.visibility = View.VISIBLE
recyclerView.addItemDecoration(ListPaddingDecoration(this@MaterialSurveyActivity))
recyclerView.adapter = MaterialSurveyAdapter(checkListQuestion)
recyclerView.layoutManager = LinearLayoutManager(this@MaterialSurveyActivity)
binding.btnSiguiente.text = getString(R.string.siguiente)
binding.btnSiguiente.setOnClickListener { onClickBtnSiguiente() }
}
private fun onClickBtnSiguiente() {
AlertDialog.Builder(this@MaterialSurveyActivity)
.setTitle(R.string.titleDialogEncuestaOperador)
.setMessage(R.string.messageDialogEncuestaOperador)
.setPositiveButton(R.string.accept) { _, _ ->
preferencesHelper.checkListProgress = Constants.HERRAMIENTA
val intent = Intent(this@MaterialSurveyActivity, HerramientaSurveyActivity::class.java)
startActivity(intent)
finish()
}
.setNegativeButton(R.string.cancel, null)
.show()
}
}

View File

@@ -0,0 +1,126 @@
package com.iesoluciones.siodrenax.activities
import android.os.Bundle
import android.view.MenuItem
import android.view.View
import android.widget.TextView
import androidx.appcompat.widget.Toolbar
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.iesoluciones.mylibrary.activities.ToolbarActivity
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.adapters.NextDayOrdersAdapter
import com.iesoluciones.siodrenax.databinding.ActivityNextServiceBinding
import com.iesoluciones.siodrenax.entities.NextDayOrder
import com.iesoluciones.siodrenax.receivers.UpdateUIReceiver
import com.iesoluciones.siodrenax.utils.ListPaddingDecoration
import com.iesoluciones.siodrenax.viewmodels.OrdersViewModel
class NextServiceActivity : ToolbarActivity(), SwipeRefreshLayout.OnRefreshListener,
UpdateUIReceiver.UiUpdateListener {
private lateinit var binding: ActivityNextServiceBinding
private lateinit var ordersViewModel: OrdersViewModel
private lateinit var orderList: List<NextDayOrder>
private var adapter: NextDayOrdersAdapter? = null
private lateinit var recyclerView: RecyclerView
private lateinit var swipeRefresh: SwipeRefreshLayout
private lateinit var tvEmpty: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityNextServiceBinding.inflate(layoutInflater)
setContentView(binding.root)
viewBinding()
@Suppress("USELESS_CAST")
val toolbar = binding.toolbar as Toolbar
toolbar.title = getString(R.string.next_day_services)
toolbarToLoad(toolbar)
ordersViewModel = ViewModelProvider(this)[OrdersViewModel::class.java]
attachToObservables()
swipeRefresh.setOnRefreshListener(this)
}
override fun onRefresh() {
ordersViewModel.isLoading.value = getString(R.string.loading)
ordersViewModel.refreshNextDayOrders()
}
override fun updateUI() {}
override fun onResume() {
super.onResume()
ordersViewModel.updateNextDayOrdersFromDB()
}
override fun onDestroy() {
super.onDestroy()
overridePendingTransition(R.anim.slide_in_rigth_airbnb, R.anim.scale_out_airbnb)
}
override fun onBackPressed() {
super.onBackPressed()
overridePendingTransition(R.anim.slide_in_rigth_airbnb, R.anim.slide_out_right_airbnb)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> {
finish()
overridePendingTransition(
R.anim.slide_in_rigth_airbnb,
R.anim.slide_out_right_airbnb
)
}
}
return super.onOptionsItemSelected(item)
}
override fun onPointerCaptureChanged(hasCapture: Boolean) {}
private fun attachToObservables() {
ordersViewModel.refreshNextDayOrders()
ordersViewModel.getNextDayOrders().observe(this) { nextDayOrders ->
orderList = nextDayOrders
if (nextDayOrders.isNotEmpty()) {
if (adapter == null) {
tvEmpty.visibility = View.GONE
recyclerView.visibility = View.VISIBLE
recyclerView.addItemDecoration(ListPaddingDecoration(this@NextServiceActivity))
adapter = NextDayOrdersAdapter(this@NextServiceActivity, nextDayOrders)
val linearLayoutManager = LinearLayoutManager(this@NextServiceActivity)
recyclerView.adapter = adapter
recyclerView.layoutManager = linearLayoutManager
adapter!!.notifyDataSetChanged()
ordersViewModel.refreshNextDayOrders()
} else {
tvEmpty.visibility = View.GONE
recyclerView.visibility = View.VISIBLE
adapter!!.setOrders(nextDayOrders)
adapter!!.notifyDataSetChanged()
}
} else {
recyclerView.visibility = View.GONE
tvEmpty.visibility = View.VISIBLE
}
if (swipeRefresh.isRefreshing) swipeRefresh.isRefreshing = false
}
}
private fun viewBinding(){
recyclerView = binding.recycler
swipeRefresh = binding.swipeRefresh
tvEmpty = binding.tvEmpty
}
}

View File

@@ -0,0 +1,124 @@
package com.iesoluciones.siodrenax.activities
import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import com.iesoluciones.mylibrary.activities.ToolbarActivity
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.adapters.OperatorAdapter
import com.iesoluciones.siodrenax.databinding.ActivityOperatorsBinding
import com.iesoluciones.siodrenax.entities.Operator
import com.iesoluciones.siodrenax.utils.Constants.Companion.OPERATOR_ID
import com.iesoluciones.siodrenax.utils.Constants.Companion.OPERATOR_NAME
import com.iesoluciones.siodrenax.utils.HelperUtil
import com.iesoluciones.siodrenax.utils.ListPaddingDecoration
import com.iesoluciones.siodrenax.viewmodels.ManagerViewModel
class OperatorsActivity : ToolbarActivity(), OnRefreshListener,
OperatorAdapter.OnOperatorSelectedListener {
private lateinit var binding: ActivityOperatorsBinding
private lateinit var managerViewModel: ManagerViewModel
var adapter: OperatorAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityOperatorsBinding.inflate(layoutInflater)
setContentView(binding.root)
managerViewModel = ViewModelProvider(this)[ManagerViewModel::class.java]
@Suppress("USELESS_CAST")
val toolbar = binding.toolbar as Toolbar
toolbar.title = getString(R.string.t_OperatorsActivity)
toolbarToLoad(toolbar)
attachToObservables()
binding.swipeRefresh.setOnRefreshListener(this)
managerViewModel.getOperators()
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.workday, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.log_out -> showLogOutConfirmation()
}
return super.onOptionsItemSelected(item)
}
private fun attachToObservables(){
managerViewModel.onCallFailure.observe(this) { throwable: Throwable? ->
if (throwable != null) {
HelperUtil().parseError(this@OperatorsActivity, throwable)
managerViewModel.onCallFailure.value = null
}
}
managerViewModel.isLoading.observe(this) { s: String? ->
if (s != null) {
binding.frameLoading.visibility = View.VISIBLE
binding.tvLoading.text = s
binding.recycler.isEnabled = false
} else {
binding.frameLoading.visibility = View.GONE
binding.recycler.isEnabled = true
}
if (binding.swipeRefresh.isRefreshing) binding.swipeRefresh.isRefreshing = false
}
managerViewModel.getOperatorList()!!.observe(this) { operators: List<Operator> ->
//Refresh adapter and recyclerView
if (adapter == null) {
adapter = OperatorAdapter(operators, this@OperatorsActivity)
binding.recycler.layoutManager = LinearLayoutManager(this@OperatorsActivity)
binding.recycler.addItemDecoration(
ListPaddingDecoration(this@OperatorsActivity)
)
binding.recycler.adapter = adapter
} else {
adapter!!.operatorList = operators
adapter!!.notifyDataSetChanged()
}
}
managerViewModel.onWorkdaySuccess.observe(this) { success: Boolean ->
if (success) {
startActivity(Intent(this@OperatorsActivity, LoginActivity::class.java))
finish()
}
}
}
override fun onRefresh() {
managerViewModel.getOperators()
}
private fun showLogOutConfirmation() {
AlertDialog.Builder(this)
.setTitle(getString(R.string.ad_t_cerrar_sesion))
.setMessage(getString(R.string.ad_m_cerrar_sesion))
.setPositiveButton(getString(R.string.ad_aceptar)) { _, _ -> managerViewModel.endWorkload() }
.setNegativeButton(getString(R.string.ad_cancelar), null)
.setCancelable(false)
.show()
}
override fun onOperatorClicked(operator: Operator) {
val intent = Intent(this@OperatorsActivity, OrdersManagerActivity::class.java)
intent.putExtra(OPERATOR_ID, operator.id)
intent.putExtra(OPERATOR_NAME,"${operator.name} ${operator.lastName} ${operator.mothersName}")
startActivity(intent)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,373 @@
package com.iesoluciones.siodrenax.activities
import android.Manifest
import android.annotation.SuppressLint
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.*
import android.telephony.PhoneNumberUtils
import android.util.Log
import android.view.MenuItem
import android.view.View
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatButton
import androidx.appcompat.widget.Toolbar
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModelProvider
import com.iesoluciones.mylibrary.activities.ToolbarActivity
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.adapters.OrdersAdapter.Companion.inFormatter
import com.iesoluciones.siodrenax.adapters.OrdersAdapter.Companion.outFormatter
import com.iesoluciones.siodrenax.databinding.ActivityOrderProgressBinding
import com.iesoluciones.siodrenax.entities.Order
import com.iesoluciones.siodrenax.repositories.OrdersRepository
import com.iesoluciones.siodrenax.utils.Constants.Companion.DEFAULT_LATITUDE
import com.iesoluciones.siodrenax.utils.Constants.Companion.DEFAULT_LONGITUDE
import com.iesoluciones.siodrenax.utils.Constants.Companion.ORDER_ID
import com.iesoluciones.siodrenax.viewmodels.OrderProgressViewModel
import java.text.ParseException
import java.util.*
import android.view.WindowManager
import android.widget.Toast
import com.iesoluciones.siodrenax.utils.*
import com.iesoluciones.siodrenax.utils.Constants.Companion.BRANCH_NAME
import com.iesoluciones.siodrenax.utils.Constants.Companion.SKETCH_PATH
class OrderProgressActivity : ToolbarActivity() {
private lateinit var binding: ActivityOrderProgressBinding
//VIEWMODELS
private lateinit var orderProgressViewModel: OrderProgressViewModel
private val ordersRepository: OrdersRepository = OrdersRepository()
private var order: Order? = null
private var street = ""
private var extNumber = ""
private var neighborhoodName = ""
private var postalCode = ""
private val googleLocate = "geo:0,0?q="
//BINDINGS
private lateinit var tvClientDemonination: TextView
private lateinit var tvOrderName: TextView
private lateinit var tvPayMethod: TextView
private lateinit var tvContact: TextView
private lateinit var tvAddress: TextView
private lateinit var tvServiceStatus: TextView
private lateinit var tvTitle: TextView
private lateinit var tvCost: TextView
private lateinit var tvOrderId: TextView
private lateinit var tvOrderScheduledTime: TextView
private lateinit var tvContactPhone: TextView
private lateinit var tvLoading: TextView
private lateinit var tvComments: TextView
private lateinit var tvVehicle: TextView
private lateinit var startServiceButton: AppCompatButton
private lateinit var linearContact: LinearLayout
private lateinit var linearComments: LinearLayout
private lateinit var getRoute: LinearLayout
private lateinit var frameLoading: FrameLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityOrderProgressBinding.inflate(layoutInflater)
setContentView(binding.root)
initializeBinding()
@Suppress("USELESS_CAST")
val toolbar = binding.toolbar as Toolbar
toolbar.title = resources.getString(R.string.t_OrderProgressActivity)
toolbarToLoad(toolbar)
enableHomeDisplay(true)
toolbar.setNavigationOnClickListener { onBackPressed() }
orderProgressViewModel = ViewModelProvider(this)[OrderProgressViewModel::class.java]
attachObservers()
order = ordersRepository.getOrderById(intent.getLongExtra(ORDER_ID, 0L))
if(order == null){
toastLong(resources.getString(R.string.error_order_detail))
return finish()
}
val orderProgress = ordersRepository.getOrderProgressById(order!!.id)
if (orderProgress != null) {
startServiceButton.isEnabled = false
startServiceButton.text = resources.getString(R.string.pendiente_envio)
}
try {
val temp: Date = inFormatter.parse(order!!.dateServiceRequest)!!
tvClientDemonination.text = order!!.clientDenomination
tvOrderScheduledTime.text = getString(
R.string.scheduled_time,
outFormatter.format(temp)
)
} catch (e: ParseException) {
e.printStackTrace()
tvClientDemonination.text = order!!.clientContactName
}
tvOrderName.text = order!!.serviceName
tvVehicle.text = order!!.vehicleCodeName
tvServiceStatus.text =
if (orderProgress != null) resources.getString(R.string.sincronizando) else order!!.serviceStatusName
if (order!!.getClientContactCellphone.trim().isNotEmpty()) {
tvContact.text = order!!.clientContactName
tvContactPhone.text = getString(
R.string.contact_phone, PhoneNumberUtils.formatNumber(
order!!.getClientContactCellphone,
"MX"
)
)
tvContactPhone.visibility = View.VISIBLE
} else {
tvContact.text = order!!.clientContactName
tvContactPhone.visibility = View.GONE
}
tvPayMethod.text = order!!.payMethodName
val drawable = StatusDrawable(order!!.serviceStatusColor1, order!!.serviceStatusColor2)
tvTitle.background = drawable
tvOrderId.text = "${order!!.idRequest}"
tvCost.text = getString(R.string.cost, order!!.cost.currencyFormat())
tvAddress.text = getString(
R.string.address,
order!!.streetAddress,
order!!.extNumberAddress,
order!!.neighborhoodNameAddress,
order!!.clientPostalCodeAddress
)
if (order!!.comments != null) {
linearComments.visibility = View.VISIBLE
tvComments.text = order!!.comments
} else linearComments.visibility = View.GONE
linearContact.setOnLongClickListener {
if (order!!.getClientContactCellphone.trim().isNotEmpty()) {
if (ActivityCompat.checkSelfPermission(this@OrderProgressActivity, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions((this@OrderProgressActivity as AppCompatActivity), arrayOf(Manifest.permission.CALL_PHONE), 3)
return@setOnLongClickListener true
}else{
val callIntent = Intent(Intent.ACTION_CALL)
callIntent.data = Uri.parse("tel:" + order!!.getClientContactCellphone.trim()) // change the number
vibrate()
startActivity(callIntent)
}
}
false
}
getRoute.setOnClickListener {
if (order!!.streetAddress != null) {
street = order!!.streetAddress!!
}
if (order!!.extNumberAddress != null) {
val en: Int
try {
en = order!!.extNumberAddress!!.toInt()
if (en != 0) {
extNumber = en.toString()
}
} catch (e: NumberFormatException) {
Log.e(TAG, "Error cachado en conversion: " + e.message)
}
}
if (order!!.neighborhoodNameAddress!= null) {
neighborhoodName = order!!.neighborhoodNameAddress!!
}
if (order!!.clientPostalCodeAddress != null) {
val pc: Int
try {
pc = order!!.clientPostalCodeAddress!!.toInt()
if (pc != 0) {
postalCode = pc.toString()
}
} catch (e: NumberFormatException) {
Log.e(TAG, "Error cachado en conversion: " + e.message)
}
}
if (street !== "" && extNumber !== "" && neighborhoodName !== "" && postalCode !== "") {
val intent = Intent(
Intent.ACTION_VIEW,
Uri.parse("$googleLocate$street+$extNumber+$neighborhoodName+$postalCode")
)
startActivity(intent)
} else {
val addressDialog =
AlertDialog.Builder(this@OrderProgressActivity, R.style.MyAlertDialogStyle)
.setTitle(resources.getString(R.string.ad_t_direccion_servicio))
.setMessage(resources.getString(R.string.ad_m_direccion_servicio))
.setCancelable(false)
.setPositiveButton(
resources.getString(R.string.ir_al_mapa)
) { _, _ ->
val intent = Intent(
Intent.ACTION_VIEW,
Uri.parse("$googleLocate$street+$extNumber+$neighborhoodName+$postalCode")
)
startActivity(intent)
}
.setNegativeButton(
resources.getString(R.string.cerrar)
) { _, _ -> }
val ad = addressDialog.create()
ad.show()
val buttonPositive = ad.getButton(DialogInterface.BUTTON_POSITIVE)
buttonPositive.setTextColor(ContextCompat.getColor(this, R.color.primary))
}
}
startServiceButton.setOnClickListener { startService() }
binding.showSketch.setOnClickListener {
if(order!!.sketchPath == null)
toast(getString(R.string.m_no_pdf_client), Toast.LENGTH_LONG)
else
goToActivity<PdfViewerActivity>{
putExtra(BRANCH_NAME, order!!.branchName)
putExtra(SKETCH_PATH, order!!.sketchPath)
}
}
}
override fun onDestroy() {
super.onDestroy()
overridePendingTransition(R.anim.slide_in_rigth_airbnb, R.anim.scale_out_airbnb)
}
override fun onBackPressed() {
super.onBackPressed()
overridePendingTransition(R.anim.slide_in_rigth_airbnb, R.anim.slide_out_right_airbnb)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> {
finish()
overridePendingTransition(
R.anim.slide_in_rigth_airbnb,
R.anim.slide_out_right_airbnb
)
}
}
return super.onOptionsItemSelected(item)
}
private fun initializeBinding(){
tvClientDemonination = binding.tvClientDenomination
tvOrderName = binding.tvOrderName
tvPayMethod = binding.tvPayMethod
tvContact = binding.tvContact
tvAddress = binding.tvAddress
tvServiceStatus = binding.tvServiceStatus
tvTitle = binding.tvTitle
tvCost = binding.tvCost
tvOrderId = binding.tvOrderId
tvOrderScheduledTime = binding.tvOrderScheduledTime
tvContactPhone = binding.tvContactPhone
tvLoading = binding.tvLoading
tvComments = binding.tvComments
tvVehicle = binding.tvVehicle
startServiceButton = binding.startServiceButton
linearContact = binding.linearContact
linearComments = binding.linearComments
getRoute = binding.getRoute
frameLoading = binding.frameLoading
}
private fun startService() {
val alertDialogBuilder: AlertDialog.Builder = AlertDialog.Builder(this)
.setCancelable(false)
.setTitle(R.string.progress_start_dialog_title)
.setMessage(R.string.progress_start_dialog_message)
.setPositiveButton(R.string.accept) { _, _ ->
toastLong(getString(R.string.processing_request))
orderProgressViewModel.startOrder(
order!!.id.toString(),
order!!.idRequest.toString(),
inFormatter.format(Date()),
DEFAULT_LATITUDE.toString(),
DEFAULT_LONGITUDE.toString()
)
}
.setNegativeButton(R.string.cancel) { dialogInterface, _ -> dialogInterface.dismiss() }
val alertDialog: AlertDialog = alertDialogBuilder.create()
alertDialog.window!!.setFlags(
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
)
alertDialog.window!!.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
alertDialog.show()
alertDialog.window!!.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
}
private fun attachObservers(){
orderProgressViewModel.onSendSuccess.observe(this) { responseBody ->
if (responseBody != null) {
if (order!!.SurveyRequired == 1)
ordersRepository.createSurveyAnswerHolders(order!!)
val intent = Intent(this@OrderProgressActivity, OrderDetailActivity::class.java)
intent.putExtra(ORDER_ID, order!!.id)
intent.flags = (Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
orderProgressViewModel.onSendSuccess.value = null
startActivity(intent)
finish()
overridePendingTransition(R.anim.slide_in_rigth_airbnb, R.anim.scale_out_airbnb)
}
}
orderProgressViewModel.onSendFailure.observe(this) { throwable ->
if (throwable != null) {
HelperUtil().parseError(this@OrderProgressActivity, throwable)
orderProgressViewModel.onSendFailure.value = null
}
}
orderProgressViewModel.isLoading.observe(this) { s ->
if (s != null) {
frameLoading.visibility = View.VISIBLE
tvLoading.text = s
} else {
frameLoading.visibility = View.GONE
}
}
}
@SuppressLint("MissingPermission")
private fun vibrate() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val vibratorManager = this.getSystemService(VIBRATOR_MANAGER_SERVICE) as VibratorManager
vibratorManager.defaultVibrator.vibrate(VibrationEffect.createOneShot(150, VibrationEffect.DEFAULT_AMPLITUDE))
}else{
(getSystemService(VIBRATOR_SERVICE) as Vibrator).vibrate(VibrationEffect.createOneShot(150, VibrationEffect.DEFAULT_AMPLITUDE))
}
}
}

View File

@@ -0,0 +1,379 @@
package com.iesoluciones.siodrenax.activities
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.FrameLayout
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import com.iesoluciones.mylibrary.activities.ToolbarActivity
import com.iesoluciones.siodrenax.*
import com.iesoluciones.siodrenax.adapters.OrdersAdapter
import com.iesoluciones.siodrenax.databinding.ActivityOrdersBinding
import com.iesoluciones.siodrenax.entities.CheckListQuestion
import com.iesoluciones.siodrenax.entities.Vehicle
import com.iesoluciones.siodrenax.interfaces.OnIncidentListener
import com.iesoluciones.siodrenax.interfaces.OnMileageListener
import com.iesoluciones.siodrenax.receivers.UpdateUIReceiver
import com.iesoluciones.siodrenax.receivers.UpdateUIReceiver.UiUpdateListener
import com.iesoluciones.siodrenax.utils.Constants
import com.iesoluciones.siodrenax.utils.Constants.Companion.DEFAULT_LATITUDE
import com.iesoluciones.siodrenax.utils.Constants.Companion.DEFAULT_LONGITUDE
import com.iesoluciones.siodrenax.utils.Constants.Companion.MILEAGE
import com.iesoluciones.siodrenax.utils.HelperUtil
import com.iesoluciones.siodrenax.utils.ListPaddingDecoration
import com.iesoluciones.siodrenax.viewmodels.IncidenceViewModel
import com.iesoluciones.siodrenax.viewmodels.OrdersViewModel
import com.iesoluciones.siodrenax.viewmodels.WorkdayStatusViewModel
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.observers.ResourceObserver
import io.reactivex.schedulers.Schedulers
import java.io.*
class OrdersActivity : ToolbarActivity(), OnMileageListener, OnIncidentListener, OnRefreshListener, UiUpdateListener {
private lateinit var binding: ActivityOrdersBinding
private var localMileage: String? = null
private lateinit var workdayStatusViewModel: WorkdayStatusViewModel
private lateinit var incidenceViewModel: IncidenceViewModel
private lateinit var ordersViewModel: OrdersViewModel
private var adapter: OrdersAdapter? = null
private lateinit var recyclerView: RecyclerView
private lateinit var swipeRefresh: SwipeRefreshLayout
private lateinit var frameHidden: FrameLayout
private lateinit var frameLoading: FrameLayout
private lateinit var tvLoading: TextView
private lateinit var tvEmpty: TextView
private var changeVehicleAux = false
private var isWaitingForExitAux = false
private lateinit var updateUIReceiver: UpdateUIReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Si se abre por medio de una notificación y hay una orden en progreso, entonces se cierra
if(intent.getBooleanExtra(Constants.EXTRA_NOTIFICATION_CLICKED, false) && preferencesHelper.orderInProgress != 0L)
return finish()
if (savedInstanceState != null) {
localMileage = savedInstanceState.getString(MILEAGE)
}
overridePendingTransition(R.anim.slide_in_rigth_airbnb, R.anim.scale_out_airbnb)
binding = ActivityOrdersBinding.inflate(layoutInflater)
setContentView(binding.root)
@Suppress("USELESS_CAST")
val toolbar = binding.toolbar as Toolbar
toolbar.title = getString(R.string.t_OrdersActivity)
toolbarToLoad(toolbar)
workdayStatusViewModel = ViewModelProvider(this)[WorkdayStatusViewModel::class.java]
incidenceViewModel = ViewModelProvider(this)[IncidenceViewModel::class.java]
ordersViewModel = ViewModelProvider(this)[OrdersViewModel::class.java]
//Bindings
recyclerView = binding.recycler
swipeRefresh = binding.swipeRefresh
frameHidden = binding.frameHidden
frameLoading = binding.frameLoading
tvLoading = binding.tvLoading
tvEmpty = binding.tvEmpty
attachToObservables()
swipeRefresh.setOnRefreshListener {
onRefresh()
}
}
override fun onRefresh() {
ordersViewModel.isLoading.value = getString(R.string.loading)
ordersViewModel.refreshOrders(filesDir.absolutePath)
}
override fun onMileageInput(mileage: String?) {
if (mileage != null) {
localMileage = mileage
HelperUtil().incidentDialogFragment(supportFragmentManager, this)
}
}
override fun onNoIncident() {
logout()
}
override fun onIncidentInput(incident: String?) {
if (incident != null) {
incidenceViewModel.sendVehicleIncidence(incident)
}
}
override fun updateUI() {
ordersViewModel.updateOrdersFromDB()
}
override fun onStart() {
super.onStart()
updateUIReceiver = UpdateUIReceiver()
updateUIReceiver.updateUIReceiver(this)
}
override fun onResume() {
super.onResume()
updateUIReceiver.registerReceiver(this)
}
override fun onStop() {
super.onStop()
updateUIReceiver.unRegisterReceiver(this)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.orders, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.log_out -> workdayStatusViewModel.validateLogOut()
R.id.sync -> HelperUtil().startSync()
R.id.change_vehicle -> workdayStatusViewModel.validateChangeVehicle()
R.id.next_day_service -> {
val nextService = Intent(this, NextServiceActivity::class.java)
startActivity(nextService)
}
}
return super.onOptionsItemSelected(item)
}
private fun attachToObservables() {
workdayStatusViewModel.isLoading.observe(this) { s ->
if (s != null) {
// This is a hidden frame over the activity used to not permit click the screen
// this avoids to click the Drenax services when screen is loading
frameHidden.visibility = View.VISIBLE
frameLoading.visibility = View.VISIBLE
tvLoading.text = s
} else {
frameLoading.visibility = View.GONE
Handler(Looper.getMainLooper()).postDelayed(
{ frameHidden.visibility = View.GONE },
500
)
}
}
workdayStatusViewModel.endWorkdaySuccess.observe(this) {
workdayStatusViewModel.logout()
}
workdayStatusViewModel.endWorkdayFailure.observe(this) { throwable ->
if (throwable != null) {
HelperUtil().parseError(this@OrdersActivity, throwable)
workdayStatusViewModel.endWorkdayFailure.value = null
}
}
ordersViewModel.getOrders().observe(this) { orders ->
if (orders.isNotEmpty()) {
if (adapter == null) {
tvEmpty.visibility = View.GONE
recyclerView.visibility = View.VISIBLE
recyclerView.addItemDecoration(ListPaddingDecoration(this@OrdersActivity))
adapter = OrdersAdapter(this@OrdersActivity, orders)
val linearLayoutManager = LinearLayoutManager(this@OrdersActivity)
recyclerView.adapter = adapter
recyclerView.layoutManager = linearLayoutManager
adapter!!.notifyDataSetChanged()
preferencesHelper.isEnableToSync = true
} else {
tvEmpty.visibility = View.GONE
recyclerView.visibility = View.VISIBLE
adapter!!.orders = orders
adapter!!.notifyDataSetChanged()
preferencesHelper.isEnableToSync = true
}
} else {
recyclerView.visibility = View.GONE
tvEmpty.visibility = View.VISIBLE
}
if (swipeRefresh.isRefreshing) swipeRefresh.isRefreshing = false
}
ordersViewModel.orderRefreshFailure.observe(this) { throwable ->
if (throwable != null) {
HelperUtil().parseError(this@OrdersActivity, throwable)
ordersViewModel.orderRefreshFailure.postValue(null)
}
if (swipeRefresh.isRefreshing) swipeRefresh.isRefreshing = false
}
workdayStatusViewModel.logOutSuccessObservable.observe(this) { logoutSuccess ->
if (logoutSuccess) {
startActivity(Intent(this@OrdersActivity, LoginActivity::class.java))
finish()
overridePendingTransition(R.anim.slide_in_rigth_airbnb, R.anim.scale_out_airbnb)
}
}
workdayStatusViewModel.isAllowedToLogOut.observe(this) { isAllowed ->
if (isAllowed != null) {
if (isAllowed) {
showLogOutDialog(this@OrdersActivity)
} else {
showCantLogOutDialog(this@OrdersActivity)
}
workdayStatusViewModel.isAllowedToLogOut.value = null
}
}
workdayStatusViewModel.isAllowedToChangeVehicle.observe(this) { isAllowed ->
if (isAllowed != null) {
if (isAllowed) {
showChangeVehicleConfirmation(this@OrdersActivity)
} else {
showCantChangeVehicleDialog(this@OrdersActivity)
}
workdayStatusViewModel.isAllowedToChangeVehicle.value = null
}
}
workdayStatusViewModel.changeVehicleSuccess.observe(this) { responseBody ->
if (responseBody != null) {
val obCheckList: Observable<List<CheckListQuestion>> = api.getCheckList(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map { checkListQuestions ->
checkListQuestionBox.put(checkListQuestions)
checkListQuestions
}
val obVehicle: Observable<List<Vehicle>> = api.getVehicle()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map { vehicle ->
vehicleBox.put(vehicle)
vehicle
}
Observable.concat(obCheckList, obVehicle).subscribe(object :
ResourceObserver<Any?>() {
override fun onNext(token: Any) {}
override fun onError(e: Throwable) {}
override fun onComplete() {
val intent = Intent(
this@OrdersActivity,
if (checkListQuestionBox.query().build().find().isNotEmpty())
RevisionSurveyActivity::class.java else WorkdayActivity::class.java
)
startActivity(intent)
finish()
}
})
}
}
incidenceViewModel.incidenceSuccess.observe(this) { response ->
if (response != null) {
logout()
}
}
}
private fun logout() {
// These validates the kind of logout with auxiliary flags
if (isWaitingForExitAux) {
workdayStatusViewModel.endWorkday(
localMileage!!,
DEFAULT_LATITUDE.toString(),
DEFAULT_LONGITUDE.toString()
)
} else if (changeVehicleAux) {
workdayStatusViewModel.changeVehicle(
localMileage!!,
DEFAULT_LATITUDE.toString(),
DEFAULT_LONGITUDE.toString()
)
}
workdayStatusViewModel.isLoading.value = getString(R.string.loading_wait_a_moment)
}
private fun showLogOutDialog(context: Context?) {
AlertDialog.Builder(context!!)
.setTitle(R.string.logout_dialog_title)
.setMessage(R.string.logout_dialog_message)
.setPositiveButton(R.string.accept) { _, _ ->
isWaitingForExitAux = true
HelperUtil().mileageDialogFragment(supportFragmentManager, this)
}
.setNegativeButton(R.string.cancel) { dialogInterface, _ -> dialogInterface.dismiss() }
.setCancelable(false)
.show()
}
private fun showCantLogOutDialog(context: Context?) {
AlertDialog.Builder(context!!)
.setTitle(R.string.cant_logout_dialog_title)
.setMessage(R.string.cant_logout_dialog_message)
.setPositiveButton(R.string.accept, null)
.setCancelable(false)
.show()
}
private fun showChangeVehicleConfirmation(context: Context?) {
AlertDialog.Builder(context!!)
.setTitle(getString(R.string.ad_t_cambiar_vehiculo))
.setMessage(getString(R.string.ad_m_cambiar_vehiculo))
.setPositiveButton(getString(R.string.ad_aceptar)) { _, _ ->
changeVehicleAux = true
HelperUtil().mileageDialogFragment(supportFragmentManager, this@OrdersActivity)
}
.setNegativeButton(R.string.ad_cancelar) { dialogInterface, _ -> dialogInterface.dismiss() }
.setCancelable(false)
.show()
}
private fun showCantChangeVehicleDialog(context: Context?) {
AlertDialog.Builder(context!!)
.setTitle(R.string.cant_logout_dialog_title)
.setMessage(R.string.cant_change_vehicle_dialog_message)
.setPositiveButton(R.string.accept) { dialogInterface, _ -> dialogInterface.dismiss() }
.setCancelable(false)
.show()
}
/*private fun showAutoCloseDialog(context: Context?) {
AlertDialog.Builder(context!!)
.setMessage(R.string.auto_close)
.setPositiveButton(R.string.accept) { _, _ ->
workdayStatusViewModel.logout()
}
.setCancelable(false)
.show()
}*/
}

View File

@@ -0,0 +1,104 @@
package com.iesoluciones.siodrenax.activities
import android.os.Bundle
import android.view.MenuItem
import android.view.View
import androidx.appcompat.widget.Toolbar
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import com.iesoluciones.mylibrary.activities.ToolbarActivity
import com.iesoluciones.siodrenax.adapters.OrdersManagerAdapter
import com.iesoluciones.siodrenax.databinding.ActivityOrdersManagerBinding
import com.iesoluciones.siodrenax.entities.Order
import com.iesoluciones.siodrenax.utils.Constants.Companion.OPERATOR_ID
import com.iesoluciones.siodrenax.utils.Constants.Companion.OPERATOR_NAME
import com.iesoluciones.siodrenax.utils.HelperUtil
import com.iesoluciones.siodrenax.utils.ListPaddingDecoration
import com.iesoluciones.siodrenax.viewmodels.ManagerViewModel
class OrdersManagerActivity : ToolbarActivity(), OnRefreshListener {
private lateinit var binding: ActivityOrdersManagerBinding
private lateinit var managerViewModel: ManagerViewModel
var adapter: OrdersManagerAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityOrdersManagerBinding.inflate(layoutInflater)
setContentView(binding.root)
@Suppress("USELESS_CAST")
val toolbar = binding.toolbar as Toolbar
toolbar.title = intent.getStringExtra(OPERATOR_NAME)
toolbarToLoad(toolbar)
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
managerViewModel = ViewModelProvider(this)[ManagerViewModel::class.java]
binding.swipeRefresh.setOnRefreshListener(this)
attachObservers()
managerViewModel.getOperatorOrders(
intent.getLongExtra(OPERATOR_ID, 0L).toString() + ""
)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> {
finish()
}
}
return super.onOptionsItemSelected(item)
}
private fun attachObservers(){
managerViewModel.onOrdersRetrieveSuccess.observe(this
) { orders: List<Order> ->
if (orders.isNotEmpty()) {
if (adapter == null) {
binding.tvEmpty.visibility = View.GONE
binding.recycler.visibility = View.VISIBLE
binding.recycler.addItemDecoration(ListPaddingDecoration(this@OrdersManagerActivity))
adapter = OrdersManagerAdapter(this@OrdersManagerActivity, orders)
val linearLayoutManager = LinearLayoutManager(this@OrdersManagerActivity)
binding.recycler.adapter = adapter
binding.recycler.layoutManager = linearLayoutManager
adapter!!.notifyDataSetChanged()
} else {
binding.tvEmpty.visibility = View.GONE
binding.recycler.visibility = View.VISIBLE
adapter!!.orders = orders
adapter!!.notifyDataSetChanged()
}
} else {
binding.recycler.visibility = View.GONE
binding.tvEmpty.visibility = View.VISIBLE
}
if (binding.swipeRefresh.isRefreshing) binding.swipeRefresh.isRefreshing = false
}
managerViewModel.onCallFailure.observe(this) { throwable: Throwable? ->
if (throwable != null) HelperUtil().parseError(this@OrdersManagerActivity, throwable)
if (binding.swipeRefresh.isRefreshing) binding.swipeRefresh.isRefreshing = false
}
managerViewModel.isLoading.observe(this
) { s: String? ->
if (s != null) {
binding.frameLoading.visibility = View.VISIBLE
binding.tvLoading.text = s
} else {
binding.frameLoading.visibility = View.GONE
}
}
}
override fun onRefresh() {
managerViewModel.getOperatorOrders(
intent.getLongExtra(OPERATOR_ID, 0L).toString()
)
}
}

View File

@@ -0,0 +1,110 @@
package com.iesoluciones.siodrenax.activities
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.pdf.PdfRenderer
import android.os.Bundle
import android.os.ParcelFileDescriptor
import android.view.View
import androidx.appcompat.widget.Toolbar
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.iesoluciones.mylibrary.activities.ToolbarActivity
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.adapters.PdfViewerAdapter
import com.iesoluciones.siodrenax.databinding.ActivityPdfViewerBinding
import com.iesoluciones.siodrenax.utils.Constants.Companion.BRANCH_NAME
import com.iesoluciones.siodrenax.utils.Constants.Companion.SKETCH_PATH
import java.io.File
import java.io.IOException
class PdfViewerActivity : ToolbarActivity() {
private lateinit var binding: ActivityPdfViewerBinding
private lateinit var recyclerView: RecyclerView
private var adapter: PdfViewerAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityPdfViewerBinding.inflate(layoutInflater)
setContentView(binding.root)
overridePendingTransition(R.anim.slide_in_rigth_airbnb, R.anim.scale_out_airbnb)
@Suppress("USELESS_CAST")
val toolbar = binding.toolbar as Toolbar
toolbar.title = intent.getStringExtra(BRANCH_NAME)
toolbarToLoad(toolbar)
enableHomeDisplay(true)
recyclerView = binding.recycler
val file = File(intent.getStringExtra(SKETCH_PATH)!!)
try {
binding.tvEmpty.visibility = View.GONE;
openPDF(file)
} catch (ioe: IOException) {
binding.tvEmpty.visibility = View.VISIBLE;
ioe.printStackTrace()
}
toolbar.setNavigationOnClickListener {
finish()
}
}
@Throws(IOException::class)
fun openPDF(file: File?) {
var fileDescriptor: ParcelFileDescriptor? = null
fileDescriptor = ParcelFileDescriptor.open(
file, ParcelFileDescriptor.MODE_READ_ONLY
)
var pdfRenderer: PdfRenderer? = null
pdfRenderer = PdfRenderer(fileDescriptor)
val pageCount: Int = pdfRenderer.pageCount
val pages : MutableList<Bitmap> = ArrayList()
for (i in 0 until pageCount){
val rendererPage: PdfRenderer.Page = pdfRenderer.openPage(i)
val rendererPageWidth: Int = rendererPage.width
val rendererPageHeight: Int = rendererPage.height
val bitmap: Bitmap = Bitmap.createBitmap(
rendererPageWidth,
rendererPageHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
canvas.drawColor(Color.WHITE)
canvas.drawBitmap(bitmap, 0f, 0f, null)
rendererPage.render(
bitmap, null, null,
PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY
)
pages.add(bitmap)
rendererPage.close()
}
pdfRenderer.close()
fileDescriptor.close()
if(adapter == null){
recyclerView.visibility = View.VISIBLE
adapter = PdfViewerAdapter(this@PdfViewerActivity, pages)
val linearLayoutManager = LinearLayoutManager(this@PdfViewerActivity)
recyclerView.adapter = adapter
recyclerView.layoutManager = linearLayoutManager
adapter!!.notifyDataSetChanged()
}else{
recyclerView.visibility = View.VISIBLE
adapter!!.pages = pages
adapter!!.notifyDataSetChanged()
}
}
}

View File

@@ -0,0 +1,155 @@
package com.iesoluciones.siodrenax.activities
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.iesoluciones.mylibrary.activities.ToolbarActivity
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.adapters.RevisionSurveyAdapter
import com.iesoluciones.siodrenax.databinding.ActivityRevisionSurveyBinding
import com.iesoluciones.siodrenax.interfaces.OnIncidentShowListener
import com.iesoluciones.siodrenax.preferencesHelper
import com.iesoluciones.siodrenax.repositories.CheckListRepository
import com.iesoluciones.siodrenax.utils.Constants
import com.iesoluciones.siodrenax.utils.HelperUtil
import com.iesoluciones.siodrenax.utils.ListPaddingDecoration
import com.iesoluciones.siodrenax.utils.toast
import com.iesoluciones.siodrenax.vehicleBox
import com.iesoluciones.siodrenax.viewmodels.IncidenceViewModel
class RevisionSurveyActivity : ToolbarActivity(), OnIncidentShowListener {
private lateinit var binding: ActivityRevisionSurveyBinding
private lateinit var recyclerView: RecyclerView
private lateinit var adapter: RevisionSurveyAdapter
private lateinit var incidenceViewModel: IncidenceViewModel
private var incidenceId: Int? = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityRevisionSurveyBinding.inflate(layoutInflater)
setContentView(binding.root)
@Suppress("USELESS_CAST")
val toolbar = binding.toolbar as Toolbar
toolbar.title = getString(R.string.revision)
toolbarToLoad(toolbar)
preferencesHelper.checkListProgress = Constants.REVISION
binding.btnSiguiente.text = getString(R.string.siguiente)
incidenceViewModel = ViewModelProvider(this)[IncidenceViewModel::class.java]
attachObservers()
val checkListQuestion = CheckListRepository().getSurveyCheckList(Constants.REVISION)
val vehicle: ArrayList<String> = ArrayList()
val vehicleIds: ArrayList<Long> = ArrayList()
vehicle.add(getString(R.string.seleccionaVehiculo))
vehicleIds.add(0)
for (v in vehicleBox.all){
vehicle.add(v.nombre)
vehicleIds.add(v.id)
}
recyclerView = binding.recycler
recyclerView.visibility = View.VISIBLE
recyclerView.addItemDecoration(ListPaddingDecoration(this@RevisionSurveyActivity))
adapter = RevisionSurveyAdapter(
binding.btnSiguiente,
checkListQuestion,
vehicle,
vehicleIds
)
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this)
binding.btnSiguiente.setOnClickListener {
recyclerView.clearFocus()
onClickBtnSiguiente()
}
}
private fun onClickBtnSiguiente() {
incidenceViewModel.getVehicleIncidence(RevisionSurveyAdapter.vehicleSelectedId)
}
private fun attachObservers() {
incidenceViewModel.isLoading.observe(this) { s ->
if (s != null) {
binding.btnSiguiente.isEnabled = false
binding.frameLoading.visibility = View.VISIBLE
binding.tvLoading.text = s
} else {
binding.btnSiguiente.isEnabled = true
binding.frameLoading.visibility = View.GONE
}
}
incidenceViewModel.incidenceSuccess.observe(this) { response ->
if (response != null) {
toast(getString(R.string.toast_incidence_solved))
alertDialog()
}
}
incidenceViewModel.incidenceFailure.observe(this) { throwable ->
if (throwable != null) {
HelperUtil().parseError(this, throwable)
incidenceViewModel.incidenceFailure.postValue(null)
}
}
incidenceViewModel.getIncidenceSuccess.observe(this) { vehicleIncidenceResponse ->
if (vehicleIncidenceResponse != null && vehicleIncidenceResponse.id > 0) {
incidenceId = vehicleIncidenceResponse.id
val incidence: String = vehicleIncidenceResponse.description
HelperUtil().incidentShowDialogFragment(supportFragmentManager, this, incidence)
} else {
alertDialog()
}
}
incidenceViewModel.getIncidenceFailure.observe(this) { throwable ->
if (throwable != null) {
HelperUtil().parseError(this, throwable)
incidenceViewModel.getIncidenceFailure.postValue(null)
}
}
}
private fun alertDialog() {
AlertDialog.Builder(this@RevisionSurveyActivity)
.setTitle(R.string.titleDialogEncuestaOperador)
.setMessage(R.string.messageDialogEncuestaOperador)
.setPositiveButton(R.string.accept) { _, _ ->
preferencesHelper.checkListProgress = Constants.MATERIAL
val intent = Intent(this@RevisionSurveyActivity, MaterialSurveyActivity::class.java)
startActivity(intent)
finish()
}
.setNegativeButton(R.string.cancel, null)
.show()
}
override fun onIncidenceSolved() {
incidenceViewModel.resolveVehicleIncidence(incidenceId.toString())
}
override fun onIncidenceNotSolved() {
alertDialog()
}
override fun onStop() {
super.onStop()
adapter.checkListQuestionBoxObserve!!.cancel()
}
}

View File

@@ -0,0 +1,122 @@
package com.iesoluciones.siodrenax.activities
import android.content.Intent
import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatButton
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.lifecycle.ViewModelProvider
import com.github.gcacace.signaturepad.views.SignaturePad
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.databinding.ActivitySignatureBinding
import com.iesoluciones.siodrenax.models.EvidenceSignatureLocal
import com.iesoluciones.siodrenax.utils.Constants.Companion.ANIMATE_FADE_IN_ALPHA
import com.iesoluciones.siodrenax.utils.Constants.Companion.ANIMATE_FADE_IN_DURATION
import com.iesoluciones.siodrenax.utils.Constants.Companion.ANIMATE_FADE_OUT_ALPHA
import com.iesoluciones.siodrenax.utils.Constants.Companion.ANIMATE_FADE_OUT_DURATION
import com.iesoluciones.siodrenax.utils.Constants.Companion.EVIDENCE_ID
import com.iesoluciones.siodrenax.utils.Constants.Companion.EVIDENCE_NAME
import com.iesoluciones.siodrenax.utils.snackBar
import com.iesoluciones.siodrenax.utils.toast
import com.iesoluciones.siodrenax.viewmodels.EvidenceViewModel
class SignatureActivity : AppCompatActivity(), SignaturePad.OnSignedListener {
private var isAtLeastSigned = false
private lateinit var binding: ActivitySignatureBinding
private lateinit var evidenceViewModel: EvidenceViewModel
private lateinit var coordinatorSignature: CoordinatorLayout
private lateinit var signaturePad: SignaturePad
private lateinit var tvTitle: TextView
private lateinit var buttonClear: AppCompatButton
private lateinit var buttonPrint: AppCompatButton
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
overridePendingTransition(R.anim.slide_in_rigth_airbnb, R.anim.scale_out_airbnb)
binding = ActivitySignatureBinding.inflate(layoutInflater)
setContentView(binding.root)
viewBinding()
signaturePad.setOnSignedListener(this)
evidenceViewModel = ViewModelProvider(this)[EvidenceViewModel::class.java]
attachObservers()
buttonClear.setOnClickListener { onClickIvClear() }
buttonPrint.setOnClickListener { onClickFabCheck() }
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.clear()
}
override fun onStartSigning() {
animateFadeOut()
}
override fun onSigned() {
isAtLeastSigned = true
animateFadeIn()
}
override fun onClear() {}
override fun onBackPressed() {}
private fun animateFadeOut() {
tvTitle.animate().alpha(ANIMATE_FADE_OUT_ALPHA)
.setDuration(ANIMATE_FADE_OUT_DURATION).start()
buttonClear.animate().alpha(ANIMATE_FADE_OUT_ALPHA)
.setDuration(ANIMATE_FADE_OUT_DURATION).start()
buttonPrint.animate().alpha(ANIMATE_FADE_OUT_ALPHA)
.setDuration(ANIMATE_FADE_OUT_DURATION).start()
}
private fun animateFadeIn() {
tvTitle.animate().alpha(ANIMATE_FADE_IN_ALPHA)
.setDuration(ANIMATE_FADE_IN_DURATION).start()
buttonPrint.animate().alpha(ANIMATE_FADE_IN_ALPHA)
.setDuration(ANIMATE_FADE_IN_DURATION).start()
buttonClear.animate().alpha(ANIMATE_FADE_IN_ALPHA)
.setDuration(ANIMATE_FADE_IN_DURATION).start()
}
private fun onClickIvClear() {
signaturePad.clear()
isAtLeastSigned = false
}
private fun onClickFabCheck() {
if (isAtLeastSigned)
evidenceViewModel.saveImage(filesDir, signaturePad.signatureBitmap)
else
snackBar(getString(R.string.s_dibuje_firma))
}
private fun attachObservers() {
evidenceViewModel.onImageSaved.observe(this) { evidenceSignatureLocal: EvidenceSignatureLocal ->
if (evidenceSignatureLocal.path != null && evidenceSignatureLocal.name != null) {
val intent = Intent()
intent.putExtra(EVIDENCE_ID, evidenceSignatureLocal.path)
intent.putExtra(EVIDENCE_NAME, evidenceSignatureLocal.name)
setResult(RESULT_OK, intent)
finish()
overridePendingTransition(R.anim.slide_in_rigth_airbnb, R.anim.scale_out_airbnb)
} else {
toast(getString(R.string.toast_ocurrio_problema))
}
}
}
private fun viewBinding() {
coordinatorSignature = binding.coordinatorSignature
signaturePad = binding.signaturePad
tvTitle = binding.tvTitle
buttonClear = binding.buttonClear
buttonPrint = binding.buttonPrint
}
}

View File

@@ -0,0 +1,104 @@
package com.iesoluciones.siodrenax.activities
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Bundle
import android.view.View.SYSTEM_UI_FLAG_FULLSCREEN
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import com.iesoluciones.siodrenax.App
import com.iesoluciones.siodrenax.databinding.ActivitySplashBinding
import com.iesoluciones.siodrenax.preferencesHelper
import com.iesoluciones.siodrenax.repositories.CheckListRepository
import com.iesoluciones.siodrenax.utils.Constants
import com.iesoluciones.siodrenax.utils.Constants.Companion.LOGGED_IN
import com.iesoluciones.siodrenax.utils.Constants.Companion.LOGGED_IN_MANAGER
import com.iesoluciones.siodrenax.utils.Constants.Companion.NO_SESSION
import com.iesoluciones.siodrenax.utils.Constants.Companion.ORDER_ID
import com.iesoluciones.siodrenax.utils.Constants.Companion.ORDER_IN_PROGRESS
import com.iesoluciones.siodrenax.utils.Constants.Companion.WORKDAY_STARTED
import com.iesoluciones.siodrenax.utils.Constants.Companion.WORKDAY_STARTED_MANAGER
import com.iesoluciones.siodrenax.utils.goToActivity
import com.iesoluciones.siodrenax.viewmodels.SplashViewModel
class SplashActivity : AppCompatActivity() {
private lateinit var binding: ActivitySplashBinding
private var splashViewModel: SplashViewModel? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySplashBinding.inflate(layoutInflater)
setContentView(binding.root)
@Suppress("DEPRECATION")
window.decorView.systemUiVisibility = (SYSTEM_UI_FLAG_FULLSCREEN)
splashViewModel = ViewModelProvider(this)[SplashViewModel::class.java]
attachObservers()
splashViewModel!!.getStartSplashObservable()
}
private fun attachObservers() {
splashViewModel!!.sessionStatusObservable.observe(this) { status ->
processStatus(status!!)
}
}
private fun processStatus(status: Int) {
when (status) {
LOGGED_IN -> {
var progressActivity: Class<*> = WorkdayActivity::class.java
if (CheckListRepository().isCheckListDone()) {
when (App.prefs!!.checkListProgress) {
Constants.REVISION -> progressActivity = RevisionSurveyActivity::class.java
Constants.MATERIAL -> progressActivity = MaterialSurveyActivity::class.java
Constants.HERRAMIENTA -> progressActivity =
HerramientaSurveyActivity::class.java
Constants.WORKDAY -> progressActivity = WorkdayActivity::class.java
else -> {
}
}
}
val intent = Intent(this@SplashActivity, progressActivity)
startActivity(intent)
finish()
}
LOGGED_IN_MANAGER -> {
goToActivity<WorkdayManagerActivity> {
flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK
}
finish()
}
WORKDAY_STARTED -> {
goToActivity<OrdersActivity> {
flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK
}
finish()
}
WORKDAY_STARTED_MANAGER -> {
goToActivity<OperatorsActivity> {
flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK
}
finish()
}
NO_SESSION -> {
goToActivity<LoginActivity> {
flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK
}
finish()
}
ORDER_IN_PROGRESS -> {
goToActivity<OrderDetailActivity> {
flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK
putExtra(ORDER_ID, preferencesHelper.orderInProgress)
}
finish()
}
}
}
}

View File

@@ -0,0 +1,162 @@
package com.iesoluciones.siodrenax.activities
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.ScrollView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.AppCompatButton
import androidx.appcompat.widget.Toolbar
import androidx.core.widget.NestedScrollView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.iesoluciones.mylibrary.activities.ToolbarActivity
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.adapters.MultipleViewHolderAdapter
import com.iesoluciones.siodrenax.databinding.ActivitySurveyBinding
import com.iesoluciones.siodrenax.entities.Order
import com.iesoluciones.siodrenax.entities.OrderProgress
import com.iesoluciones.siodrenax.models.Question
import com.iesoluciones.siodrenax.repositories.OrdersRepository
import com.iesoluciones.siodrenax.repositories.SurveyRepository
import com.iesoluciones.siodrenax.utils.Constants.Companion.DRIVE_READER
import com.iesoluciones.siodrenax.utils.Constants.Companion.IDSERVICETYPEDOMESTIC
import com.iesoluciones.siodrenax.utils.Constants.Companion.ORDER_ID
import com.iesoluciones.siodrenax.utils.Constants.Companion.PDF_URL
import com.iesoluciones.siodrenax.utils.toast
class SurveyActivity : ToolbarActivity() {
private var orderId: Long = 0
private var isDomestic = false
private lateinit var skipItem: MenuItem
private lateinit var acceptItem: MenuItem
private lateinit var order: Order
private lateinit var orderProgress: OrderProgress
private lateinit var binding: ActivitySurveyBinding
private lateinit var ordersRepository: OrdersRepository
private lateinit var surveyRepository: SurveyRepository
private lateinit var recycler: RecyclerView
private lateinit var svWebView: ScrollView
private lateinit var nsvSurvey: NestedScrollView
private lateinit var webview: WebView
private lateinit var btnPrivacyAdvice: AppCompatButton
private lateinit var btnSaveSurvey: AppCompatButton
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
overridePendingTransition(R.anim.slide_in_rigth_airbnb, R.anim.scale_out_airbnb)
binding = ActivitySurveyBinding.inflate(layoutInflater)
setContentView(binding.root)
@Suppress("USELESS_CAST")
val toolbar = binding.toolbar as Toolbar
toolbar.title = getString(R.string.t_SurveyActivity)
toolbarToLoad(toolbar)
viewBinding()
ordersRepository = OrdersRepository()
surveyRepository = SurveyRepository()
orderId = intent.getLongExtra(ORDER_ID, 0L)
order = ordersRepository.getOrderById(orderId)!!
orderProgress = ordersRepository.getOrderProgressById(orderId)!!
isDomestic = order.idServiceType == IDSERVICETYPEDOMESTIC
val surveyQuestions: List<Question> = surveyRepository.getSurveyQuestions(isDomestic)
val adapter = MultipleViewHolderAdapter(surveyQuestions, order)
recycler.layoutManager = LinearLayoutManager(this)
recycler.setHasFixedSize(true)
recycler.adapter = adapter
btnPrivacyAdvice.setOnClickListener { onPrivacyAdviceClick() }
btnSaveSurvey.setOnClickListener { onSurveyButtonClick() }
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.survey, menu)
skipItem = menu.findItem(R.id.skip)
acceptItem = menu.findItem(R.id.accept)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.skip -> AlertDialog.Builder(this)
.setTitle(getString(R.string.ad_t_omitir_encuesta))
.setMessage(getString(R.string.ad_m_omitir_encuesta))
.setPositiveButton(R.string.accept) { _, _ ->
orderProgress.isOnSurvey = false
ordersRepository.saveOrderProgress(orderProgress)
surveyRepository.deleteAllSavedAnswers(order)
setResult(RESULT_OK)
finish()
overridePendingTransition(R.anim.slide_in_rigth_airbnb, R.anim.scale_out_airbnb)
}
.setNegativeButton(R.string.cancel, null)
.show()
R.id.accept -> {
svWebView.visibility = View.GONE
nsvSurvey.visibility = View.VISIBLE
acceptItem.isVisible = false
skipItem.isVisible = true
}
}
return super.onOptionsItemSelected(item)
}
override fun onBackPressed() {}
@SuppressLint("SetJavaScriptEnabled")
private fun onPrivacyAdviceClick() {
skipItem.isVisible = false
acceptItem.isVisible = true
webview.settings.javaScriptEnabled = true
webview.loadUrl(DRIVE_READER + PDF_URL)
webview.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
view.loadUrl(url)
return true
}
}
nsvSurvey.visibility = View.GONE
svWebView.visibility = View.VISIBLE
}
private fun onSurveyButtonClick() {
recycler.requestFocus()
recycler.requestFocusFromTouch()
btnSaveSurvey.isEnabled = false
if (surveyRepository.validateAnswers(order)) {
btnSaveSurvey.isEnabled = true
orderProgress.isOnSurvey = false
orderProgress.shouldSendSurvey = true
ordersRepository.saveOrderProgress(orderProgress)
setResult(RESULT_OK)
finish()
overridePendingTransition(R.anim.slide_in_rigth_airbnb, R.anim.scale_out_airbnb)
} else {
toast(R.string.toast_faltan_campos)
btnSaveSurvey.isEnabled = true
}
}
private fun viewBinding() {
recycler = binding.recycler
svWebView = binding.svWebView
nsvSurvey = binding.nsvSurvey
webview = binding.webview
btnPrivacyAdvice = binding.btnPrivacyAdvice
btnSaveSurvey = binding.btnSaveSurvey
}
}

View File

@@ -0,0 +1,137 @@
package com.iesoluciones.siodrenax.activities
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.telephony.PhoneNumberUtils
import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar
import androidx.lifecycle.ViewModelProvider
import com.iesoluciones.mylibrary.activities.ToolbarActivity
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.databinding.ActivityWorkdayBinding
import com.iesoluciones.siodrenax.interfaces.OnMileageListener
import com.iesoluciones.siodrenax.preferencesHelper
import com.iesoluciones.siodrenax.repositories.WorkdayRepository
import com.iesoluciones.siodrenax.utils.CircularProgressButton
import com.iesoluciones.siodrenax.utils.Constants.Companion.DEFAULT_LATITUDE
import com.iesoluciones.siodrenax.utils.Constants.Companion.DEFAULT_LONGITUDE
import com.iesoluciones.siodrenax.utils.HelperUtil
import com.iesoluciones.siodrenax.viewmodels.WorkdayStatusViewModel
import java.text.SimpleDateFormat
import java.util.*
class WorkdayActivity : ToolbarActivity(), CircularProgressButton.ProgressListener, OnMileageListener {
private lateinit var binding: ActivityWorkdayBinding
private lateinit var workdayStatusViewModel: WorkdayStatusViewModel
private lateinit var tokenFirebase: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityWorkdayBinding.inflate(layoutInflater)
setContentView(binding.root)
@Suppress("USELESS_CAST")
val toolbar = binding.toolbar as Toolbar
toolbar.title = getString(R.string.t_WorkdayActivity)
toolbarToLoad(toolbar)
val user = WorkdayRepository().getUser()
binding.tvUsername.text = getString(R.string.username, user.username, user.lastName, user.mothersName)
binding.tvInitials.text = user.username.substring(0, 1)
binding.tvPhone.text = PhoneNumberUtils.formatNumber(user.phone, "MX")
val inFormatter = SimpleDateFormat("dd 'de' MMM 'de' yyyy", Locale.getDefault())
binding.tvDate.text = inFormatter.format(Date())
binding.iniciarJornadaButton.progressListener = this
workdayStatusViewModel = ViewModelProvider(this)[WorkdayStatusViewModel::class.java]
attachToObservables()
tokenFirebase = preferencesHelper.tokenFirebase!!
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.workday, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.log_out -> workdayStatusViewModel.logout()
}
return super.onOptionsItemSelected(item)
}
override fun onProgressStart() {}
override fun onProgressFinish() {
HelperUtil().mileageDialogFragment(supportFragmentManager, this)
}
override fun onMileageInput(mileage: String?) {
if (mileage != null) {
workdayStatusViewModel.isLoading.value = getString(R.string.loading_wait_a_moment)
workdayStatusViewModel.startWorkday(
tokenFirebase,
mileage,
DEFAULT_LATITUDE.toString(),
DEFAULT_LONGITUDE.toString(),
filesDir.absolutePath
)
}
}
private fun attachToObservables() {
workdayStatusViewModel.isLoading.observe(this) { s ->
if (s != null) {
binding.frameLoading.visibility = View.VISIBLE
binding.tvLoading.text = s
binding.iniciarJornadaButton.deactivate()
} else {
binding.frameLoading.visibility = View.GONE
binding.iniciarJornadaButton.activate()
}
}
workdayStatusViewModel.logOutSuccessObservable.observe(this) { logoutSuccess ->
if (logoutSuccess != null) if (logoutSuccess) {
startActivity(Intent(this@WorkdayActivity, LoginActivity::class.java))
finish()
}
}
workdayStatusViewModel.startWorkdaySuccess.observe(this) {
startActivity(Intent(this@WorkdayActivity, OrdersActivity::class.java))
finish()
}
workdayStatusViewModel.startWorkdayFailure.observe(this) { throwable ->
if (throwable != null) {
HelperUtil().parseError(this, throwable)
workdayStatusViewModel.startWorkdayFailure.value = null
}
}
workdayStatusViewModel.endWorkdaySuccess.observe(this) {
startActivity(Intent(this@WorkdayActivity, LoginActivity::class.java))
finish()
}
workdayStatusViewModel.endWorkdayFailure.observe(this) { throwable ->
HelperUtil().parseError(this, throwable)
}
}
/*private fun showAutoCloseDialog(context: Context?) {
AlertDialog.Builder(context!!)
.setMessage(R.string.auto_close)
.setPositiveButton(R.string.accept) { _, _ ->
workdayStatusViewModel.logout()
}
.setCancelable(false)
.show()
}*/
}

View File

@@ -0,0 +1,105 @@
package com.iesoluciones.siodrenax.activities
import android.content.Intent
import android.os.Bundle
import android.telephony.PhoneNumberUtils
import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.appcompat.widget.Toolbar
import androidx.lifecycle.ViewModelProvider
import com.iesoluciones.mylibrary.activities.ToolbarActivity
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.databinding.ActivityWorkdayBinding
import com.iesoluciones.siodrenax.preferencesHelper
import com.iesoluciones.siodrenax.repositories.ManagerRepository
import com.iesoluciones.siodrenax.utils.CircularProgressButton
import com.iesoluciones.siodrenax.utils.HelperUtil
import com.iesoluciones.siodrenax.utils.goToActivity
import com.iesoluciones.siodrenax.viewmodels.ManagerViewModel
import java.text.SimpleDateFormat
import java.util.*
class WorkdayManagerActivity : ToolbarActivity(), CircularProgressButton.ProgressListener {
private lateinit var binding: ActivityWorkdayBinding
private lateinit var managerViewModel: ManagerViewModel
private lateinit var managerRepository: ManagerRepository
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityWorkdayBinding.inflate(layoutInflater)
setContentView(binding.root)
@Suppress("USELESS_CAST")
val toolbar = binding.toolbar as Toolbar
toolbar.title = getString(R.string.t_WorkdayManagerActivity)
toolbarToLoad(toolbar)
managerRepository = ManagerRepository()
binding.iniciarJornadaButton.progressListener = this
managerViewModel = ViewModelProvider(this)[ManagerViewModel::class.java]
attachToObservables()
}
override fun onProgressStart() {}
override fun onProgressFinish() {
managerViewModel.startWorkload()
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.workday, menu)
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.log_out -> {
managerRepository.logOut()
goToActivity<LoginActivity> {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
finish()
}
}
return super.onOptionsItemSelected(item)
}
private fun attachToObservables() {
managerViewModel.onCallFailure.observe(this) { throwable: Throwable? ->
if (throwable != null) {
HelperUtil().parseError(this@WorkdayManagerActivity, throwable)
managerViewModel.onCallFailure.value = null
}
}
managerViewModel.isLoading.observe(this) { s: String? ->
if (s != null) {
binding.frameLoading.visibility = View.VISIBLE
binding.tvLoading.text = s
binding.iniciarJornadaButton.deactivate()
} else {
binding.frameLoading.visibility = View.GONE
}
}
managerViewModel.onWorkdaySuccess.observe(this) { success: Boolean ->
if (success) {
preferencesHelper.workdayStarted = true
goToActivity<OperatorsActivity> {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
}
}
managerViewModel.getUser()!!.observe(this) { (_, _, username, lastName, mothersName, phone) ->
binding.tvUsername.text = getString(R.string.username, username, lastName, mothersName)
binding.tvInitials.text = username.substring(0, 1)
binding.tvPhone.text = PhoneNumberUtils.formatNumber(phone, "MX")
val inFormatter = SimpleDateFormat("dd 'de' MMM 'de' yyyy", Locale.getDefault())
binding.tvDate.text = inFormatter.format(Date())
}
}
}

View File

@@ -0,0 +1,111 @@
package com.iesoluciones.siodrenax.adapters
import android.text.Editable
import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.checkListQuestionBox
import com.iesoluciones.siodrenax.databinding.ViewholderHerramientaBinding
import com.iesoluciones.siodrenax.entities.CheckListQuestion
import com.iesoluciones.siodrenax.entities.CheckListQuestion_
import com.iesoluciones.siodrenax.preferencesHelper
import com.iesoluciones.siodrenax.utils.Constants
import io.objectbox.android.AndroidScheduler
import io.objectbox.kotlin.equal
import io.objectbox.reactive.DataSubscription
class HerramientaSurveyAdapter(
private var checkListQuestionList: List<CheckListQuestion>
) : RecyclerView.Adapter<HerramientaSurveyAdapter.MyViewHolder>() {
var checkListQuestionBoxObserve: DataSubscription? = null
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
checkListQuestionBoxObserve = checkListQuestionBox.query(
CheckListQuestion_.tipo equal Constants.HERRAMIENTA
)
.build()
.subscribe()
.on(AndroidScheduler.mainThread())
.observer { checkListQuestion -> checkListQuestionList = checkListQuestion }
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val binding = ViewholderHerramientaBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, i: Int) {
holder.bind(checkListQuestionList[i])
}
override fun getItemCount() = checkListQuestionList.size
class MyViewHolder(var binding: ViewholderHerramientaBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(checkListQuestion: CheckListQuestion) = with(itemView) {
itemView.tag = checkListQuestion.id
binding.tvQuestionTitle.text = checkListQuestion.nombre
binding.lLCheck.visibility =
if (checkListQuestion.tipoCheckBox == 1) View.VISIBLE else View.GONE
binding.frameEdit.visibility =
if (checkListQuestion.tipoText == 1) View.VISIBLE else View.GONE
binding.eTComentario.setText(checkListQuestion.respuestaText)
binding.cBQuestion.isChecked = checkListQuestion.respuestaCheckBox
val isEnabled = preferencesHelper.isEnableHerramientaSurvey
binding.eTComentario.isEnabled = isEnabled
binding.cBQuestion.isEnabled = isEnabled
if (!isEnabled) {
binding.tvQuestionTitle.setTextColor(
ContextCompat.getColor(
context,
R.color.letraGrisBloqueado
)
)
} else {
listeners()
}
}
private fun listeners() {
val id = itemView.tag.toString().toLong()
val objCheck: CheckListQuestion = checkListQuestionBox.get(id)
if (objCheck.tipoText == 1) {
binding.eTComentario.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
override fun afterTextChanged(s: Editable) {
objCheck.respuestaText = s.toString()
checkListQuestionBox.put(objCheck)
}
})
}
if (objCheck.tipoCheckBox == 1) {
binding.cBQuestion.setOnCheckedChangeListener { _, isChecked ->
objCheck.respuestaCheckBox = isChecked
checkListQuestionBox.put(objCheck)
}
}
}
}
}

View File

@@ -0,0 +1,76 @@
package com.iesoluciones.siodrenax.adapters
import android.text.Editable
import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.iesoluciones.siodrenax.checkListQuestionBox
import com.iesoluciones.siodrenax.databinding.ViewholderMaterialBinding
import com.iesoluciones.siodrenax.entities.CheckListQuestion
class MaterialSurveyAdapter(
private val checkListQuestionList: List<CheckListQuestion>
): RecyclerView.Adapter<MaterialSurveyAdapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val binding = ViewholderMaterialBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, i: Int) {
holder.bind(checkListQuestionList[i])
}
override fun getItemCount() = checkListQuestionList.size
class MyViewHolder(var binding: ViewholderMaterialBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(checkListQuestion: CheckListQuestion) = with(itemView) {
itemView.tag = checkListQuestion.id
binding.tvQuestionTitle.text = checkListQuestion.nombre
binding.lLCheck.visibility = if (checkListQuestion.tipoCheckBox == 1) View.VISIBLE else View.GONE
binding.frameEdit.visibility = if (checkListQuestion.tipoText == 1) View.VISIBLE else View.GONE
binding.eTComentario.setText(checkListQuestion.respuestaText)
binding.cBQuestion.isChecked = checkListQuestion.respuestaCheckBox
listeners()
}
private fun listeners() {
val id = itemView.tag.toString().toLong()
val objCheck: CheckListQuestion = checkListQuestionBox.get(id)
binding.eTComentario.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(
s: CharSequence,
start: Int,
count: Int,
after: Int
) {
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (objCheck.tipoText == 1) {
objCheck.respuestaText = binding.eTComentario.text.toString()
checkListQuestionBox.put(objCheck)
}
}
override fun afterTextChanged(s: Editable) {}
})
binding.cBQuestion.setOnCheckedChangeListener { _, isChecked ->
objCheck.respuestaCheckBox = isChecked
checkListQuestionBox.put(objCheck)
}
}
}
}

View File

@@ -0,0 +1,355 @@
package com.iesoluciones.siodrenax.adapters
import android.app.Activity
import android.app.DatePickerDialog
import android.text.Editable
import android.text.InputType
import android.text.TextWatcher
import android.util.Patterns
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.View.OnFocusChangeListener
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import androidx.appcompat.widget.AppCompatCheckBox
import androidx.recyclerview.widget.RecyclerView
import com.iesoluciones.siodrenax.App
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.databinding.ViewholderAnswerOpenBinding
import com.iesoluciones.siodrenax.databinding.ViewholderCheckboxAnswerBinding
import com.iesoluciones.siodrenax.entities.Order
import com.iesoluciones.siodrenax.models.Answer
import com.iesoluciones.siodrenax.models.Question
import com.iesoluciones.siodrenax.repositories.SurveyRepository
import com.iesoluciones.siodrenax.utils.Constants.Companion.ANSWER_CHECKBOX
import com.iesoluciones.siodrenax.utils.Constants.Companion.ANSWER_CURRENCY
import com.iesoluciones.siodrenax.utils.Constants.Companion.ANSWER_DATE
import com.iesoluciones.siodrenax.utils.Constants.Companion.ANSWER_EMAIL
import com.iesoluciones.siodrenax.utils.Constants.Companion.ANSWER_NUMBER
import com.iesoluciones.siodrenax.utils.Constants.Companion.ANSWER_TEXT
import com.iesoluciones.siodrenax.utils.Constants.Companion.CERO
import com.iesoluciones.siodrenax.utils.Constants.Companion.IDSERVICETYPEDOMESTIC
import java.text.NumberFormat
import java.util.*
class MultipleViewHolderAdapter(
private var questionList: List<Question>,
private val order: Order
) :
RecyclerView.Adapter<MultipleViewHolderAdapter.AnswerViewHolder>() {
private val isDomestic = order.idServiceType == IDSERVICETYPEDOMESTIC
private val surveyRepository = SurveyRepository()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AnswerViewHolder {
when (viewType) {
ANSWER_CHECKBOX -> return CheckBoxViewHolder(
ViewholderCheckboxAnswerBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
ANSWER_CURRENCY, ANSWER_DATE, ANSWER_EMAIL, ANSWER_NUMBER, ANSWER_TEXT -> return OpenAnswerViewHolder(
ViewholderAnswerOpenBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
),
viewType
)
}
throw RuntimeException("Uncaught viewholder Type")
}
override fun onBindViewHolder(holder: AnswerViewHolder, position: Int) {
holder.bindViews(questionList[position])
}
override fun getItemCount(): Int {
return questionList.size
}
override fun getItemViewType(position: Int): Int {
return getViewType(questionList[position])
}
abstract class AnswerViewHolder(itemView: View) :
RecyclerView.ViewHolder(itemView) {
abstract fun bindViews(question: Question)
abstract fun cleanOnRecycled()
}
inner class CheckBoxViewHolder(var binding: ViewholderCheckboxAnswerBinding) :
AnswerViewHolder(binding.root), View.OnClickListener {
private var tvQuestionTitle = binding.tvQuestionTitle
private var linearAnswer = binding.linearAnswer
private lateinit var answers: List<Answer>
private lateinit var answer: Answer
private var checkBoxes: MutableList<AppCompatCheckBox> = ArrayList()
private lateinit var question: Question
override fun bindViews(question: Question) {
this.question = question
if (question.getShowNumber() == 0)
tvQuestionTitle.text = question.getTitle()
else
tvQuestionTitle.text = App.context!!.resources.getString(
R.string.question_title,
(adapterPosition + 1),
question.getTitle()
)
answers = surveyRepository.getAnswers(question, isDomestic)
for (answer in answers) {
val checkBox = AppCompatCheckBox(itemView.context)
checkBox.layoutParams = ViewGroup.LayoutParams(
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
ViewGroup.LayoutParams.WRAP_CONTENT.toFloat(),
itemView.context.resources.displayMetrics
)
.toInt(),
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
ViewGroup.LayoutParams.WRAP_CONTENT.toFloat(),
itemView.context.resources.displayMetrics
)
.toInt()
)
checkBox.text = answer.getTitle()
checkBox.isChecked = false
checkBox.tag = answer
checkBox.setOnClickListener(this)
val savedAnswer = surveyRepository.getQuestionSavedAnswer(question, order)
if (savedAnswer != null && savedAnswer != "" && savedAnswer.toLong() == answer.getId()) {
this.answer = answer
checkBox.isChecked = true
}
linearAnswer.addView(checkBox)
checkBoxes.add(checkBox)
}
}
override fun cleanOnRecycled() {
linearAnswer.removeAllViews()
}
override fun onClick(v: View) {
if (!(v as AppCompatCheckBox).isChecked) {
surveyRepository.saveQuestionAnswer(question, order, null, false)
} else {
answer = v.getTag() as Answer
surveyRepository.saveQuestionAnswer(
question,
order,
answer.getId().toString() + "",
false
)
}
for (checkBox in checkBoxes)
if (checkBox.tag !== answer)
checkBox.isChecked = false
}
}
inner class OpenAnswerViewHolder(
var binding: ViewholderAnswerOpenBinding,
private var viewType: Int
) :
AnswerViewHolder(binding.root), View.OnClickListener {
private var tvQuestionTitle = binding.tvQuestionTitle
private var frameDate = binding.frameDate
private var tvDate = binding.tvDate
private var editAnswer = binding.editAnswer
private var linearQuestion = binding.linearQuestion
private lateinit var question: Question
//Calendario para obtener fecha & hora
private val c = Calendar.getInstance()
//Variables para obtener la fecha
private val mes = c[Calendar.MONTH]
private val dia = c[Calendar.DAY_OF_MONTH]
private val anio = c[Calendar.YEAR]
override fun bindViews(question: Question) {
this.question = question
if (question.getShowNumber() == 0)
tvQuestionTitle.text = question.getTitle()
else
tvQuestionTitle.text = App.context!!.resources.getString(
R.string.question_title,
(adapterPosition + 1),
question.getTitle()
)
when (viewType) {
ANSWER_CURRENCY -> {
//Currency TextWatcher, set
editAnswer.inputType = InputType.TYPE_CLASS_NUMBER
editAnswer.addTextChangedListener(CurrencyTextWatcher())
editAnswer.setText("0")
editAnswer.onFocusChangeListener =
OnFocusChangeListener { _, hasFocus ->
if (!hasFocus) {
var formatted = editAnswer.text.toString()
formatted = formatted.replace(",".toRegex(), "")
formatted = formatted.replace("\\$".toRegex(), "")
surveyRepository.saveQuestionAnswer(
question,
order,
formatted,
true
)
}
}
}
ANSWER_DATE -> {
//Block clicks to show date picker, and format it
editAnswer.visibility = View.GONE
frameDate.visibility = View.VISIBLE
linearQuestion.setOnClickListener(this)
}
ANSWER_EMAIL -> {
//Email TextWatcher
editAnswer.inputType = InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
editAnswer.imeOptions = EditorInfo.IME_ACTION_NEXT
editAnswer.onFocusChangeListener =
OnFocusChangeListener { _, hasFocus ->
if (!hasFocus) {
if (!isValidEmail(editAnswer.text.toString())) editAnswer.error =
"Correo inválido" else surveyRepository.saveQuestionAnswer(
question, order, editAnswer.text.toString(), true
)
}
}
}
ANSWER_NUMBER -> //Number formatter
editAnswer.inputType = InputType.TYPE_CLASS_NUMBER
ANSWER_TEXT -> {
//Text TextWatcher
editAnswer.imeOptions = EditorInfo.IME_ACTION_NEXT
editAnswer.onFocusChangeListener =
OnFocusChangeListener { _, hasFocus ->
var answers = editAnswer.text.toString()
answers = answers.replace(" ", "")
if (!hasFocus && answers != "") {
surveyRepository.saveQuestionAnswer(
question,
order,
editAnswer.text.toString(),
true
)
}
}
if (!editAnswer.hasFocus() && editAnswer.text.toString() == "")
surveyRepository.saveQuestionAnswer(question, order, null, true)
}
}
val savedAnswer = surveyRepository.getQuestionSavedAnswer(question, order)
if (viewType == ANSWER_DATE)
tvDate.text = savedAnswer
else
editAnswer.setText(savedAnswer)
}
override fun cleanOnRecycled() {}
private fun isValidEmail(target: String?): Boolean {
return if (target == null) {
false
} else {
//android Regex to check the email address Validation
Patterns.EMAIL_ADDRESS.matcher(target).matches()
}
}
private fun obtenerFecha() {
val recogerFecha = DatePickerDialog(
itemView.context,
{ _, year, month, dayOfMonth -> //Esta variable lo que realiza es aumentar en uno el mes ya que comienza desde 0 = enero
val mesActual = month + 1
//Formateo el día obtenido: antepone el 0 si son menores de 10
val diaFormateado =
if (dayOfMonth < 10) CERO + dayOfMonth.toString() else dayOfMonth.toString()
//Formateo el mes obtenido: antepone el 0 si son menores de 10
val mesFormateado =
if (mesActual < 10) CERO + mesActual.toString() else mesActual.toString()
//Muestro la fecha con el formato deseado
val dateFormatted = App.context!!.resources.getString(
R.string.date_format,
year.toString(),
mesFormateado,
diaFormateado
)
tvDate.text = dateFormatted
surveyRepository.saveQuestionAnswer(
question,
order,
dateFormatted,
true
)
}, //Estos valores deben ir en ese orden, de lo contrario no mostrara la fecha actual
anio, mes, dia
)
//Muestro el widget
recogerFecha.show()
}
internal inner class CurrencyTextWatcher : TextWatcher {
private var current = ""
@Synchronized
override fun afterTextChanged(s: Editable) {
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (s.toString() != current) {
editAnswer.removeTextChangedListener(this)
val cleanString = s.toString().replace("[$,.]".toRegex(), "")
var parsed = 0.0
try {
parsed = cleanString.toDouble()
} catch (ignored: NumberFormatException) {
}
val formatted = NumberFormat.getCurrencyInstance().format(parsed / 100)
current = formatted
editAnswer.setText(formatted)
editAnswer.setSelection(formatted.length)
editAnswer.addTextChangedListener(this)
}
}
}
override fun onClick(v: View) {
val imm =
itemView.context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(itemView.windowToken, 0)
obtenerFecha()
}
}
private fun getViewType(question: Question): Int {
return when (surveyRepository.getAnswers(question, isDomestic)[0].getType()) {
"Checkbox" -> ANSWER_CHECKBOX
"Fecha" -> ANSWER_DATE
"Email" -> ANSWER_EMAIL
"Numero" -> ANSWER_NUMBER
"Moneda" -> ANSWER_CURRENCY
"Texto" -> ANSWER_TEXT
else -> throw RuntimeException("Question Type Not Supported")
}
}
}

View File

@@ -0,0 +1,159 @@
package com.iesoluciones.siodrenax.adapters
import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.Context.VIBRATOR_SERVICE
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.VibrationEffect
import android.os.Vibrator
import android.os.VibratorManager
import android.telephony.PhoneNumberUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.recyclerview.widget.RecyclerView
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.databinding.ViewholderOrderBinding
import com.iesoluciones.siodrenax.entities.NextDayOrder
import com.iesoluciones.siodrenax.utils.StatusDrawable
import com.iesoluciones.siodrenax.utils.currencyFormat
import java.text.SimpleDateFormat
import java.util.*
class NextDayOrdersAdapter(
private val context: Context,
private var orders: List<NextDayOrder>
) : RecyclerView.Adapter<NextDayOrdersAdapter.ViewHolder>() {
companion object {
val inFormatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
val outFormatter = SimpleDateFormat("HH:mm", Locale.getDefault())
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding =
ViewholderOrderBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding, context)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(orders[position])
}
override fun getItemCount(): Int {
return orders.size
}
fun setOrders(orders: List<NextDayOrder>) {
this.orders = orders
}
class ViewHolder(
val binding: ViewholderOrderBinding,
val context: Context
) : RecyclerView.ViewHolder(binding.root) {
private val tvClientDenomination = binding.tvClientDenomination
private val tvOrderName = binding.tvOrderName
private val tvPayMethod = binding.tvPayMethod
private val tvContact = binding.tvContact
private val tvAddress = binding.tvAddress
private val tvServiceStatus = binding.tvServiceStatus
private val tvTitle = binding.tvTitle
private val tvCost = binding.tvCost
private val tvOrderId = binding.tvOrderId
private val tvOrderScheduledTime = binding.tvOrderScheduledTime
private val tvContactPhone = binding.tvContactPhone
private val linearContact = binding.linearContact
private val linearComments = binding.linearComments
private val tvComments = binding.tvComments
private val tvVehicle = binding.tvVehicle
fun bind(order: NextDayOrder) = with(itemView) {
try {
val temp: Date = inFormatter.parse(order.dateServiceRequest)!!
inFormatter.parse(order.dateServiceRequest)!!
tvClientDenomination.text = order.clientDenomination
tvOrderScheduledTime.text = context.resources.getString(
R.string.scheduled_time, outFormatter.format(
temp
)
)
} catch (e: Exception) {
e.printStackTrace()
tvClientDenomination.text = order.clientContactName
tvOrderScheduledTime.text =
context.resources.getString(R.string.scheduled_time_error)
}
if (order.getClientContactCellphone.trim().isNotEmpty()) {
tvContact.text = order.clientContactName
tvContactPhone.text = context.resources.getString(
R.string.contact_phone, PhoneNumberUtils.formatNumber(
order.getClientContactCellphone,
"MX"
)
)
tvContactPhone.visibility = View.VISIBLE
} else {
tvContact.text = order.clientContactName
tvContactPhone.visibility = View.GONE
}
if (order.comments != null) {
linearComments.visibility = View.VISIBLE
tvComments.text = order.comments
} else
linearComments.visibility = View.GONE
tvOrderName.text = order.serviceName
tvServiceStatus.text = order.serviceStatusName
tvPayMethod.text = order.payMethodName
tvVehicle.text = order.vehicleCodeName
tvOrderId.text = "${order.idRequest}"
tvCost.text = context.resources.getString(R.string.cost, order.cost.currencyFormat())
tvAddress.text = context.resources.getString(
R.string.address,
order.streetAddress,
order.extNumberAddress,
order.neighborhoodNameAddress,
order.clientPostalCodeAddress
)
val drawable = StatusDrawable(order.serviceStatusColor1, order.serviceStatusColor2)
tvTitle.background = drawable
linearContact.setOnLongClickListener {
if (order.getClientContactCellphone.trim().isNotEmpty()) {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions((itemView.context as AppCompatActivity), arrayOf(Manifest.permission.CALL_PHONE), 3)
return@setOnLongClickListener true
} else {
val callIntent = Intent(Intent.ACTION_CALL)
callIntent.data = Uri.parse("tel:" + order.getClientContactCellphone.trim()) // change the number
vibrate()
context.startActivity(callIntent)
}
}
false
}
}
@SuppressLint("MissingPermission")
private fun vibrate() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val vibratorManager = context.getSystemService(AppCompatActivity.VIBRATOR_MANAGER_SERVICE) as VibratorManager
vibratorManager.defaultVibrator.vibrate(VibrationEffect.createOneShot(150, VibrationEffect.DEFAULT_AMPLITUDE))
}else{
(context.getSystemService(VIBRATOR_SERVICE) as Vibrator).vibrate(VibrationEffect.createOneShot(150, VibrationEffect.DEFAULT_AMPLITUDE))
}
}
}
}

View File

@@ -0,0 +1,68 @@
package com.iesoluciones.siodrenax.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.databinding.ViewholderOperatorBinding
import com.iesoluciones.siodrenax.entities.Operator
import com.iesoluciones.siodrenax.utils.HelperUtil
class OperatorAdapter(
var operatorList: List<Operator>,
private val listener: OnOperatorSelectedListener
) : RecyclerView.Adapter<OperatorAdapter.ViewHolder>() {
interface OnOperatorSelectedListener {
fun onOperatorClicked(operator: Operator)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding =
ViewholderOperatorBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding, listener)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(operatorList[position], position%2 == 0)
}
override fun getItemCount(): Int {
return operatorList.size
}
class ViewHolder(
val binding: ViewholderOperatorBinding,
val listener: OnOperatorSelectedListener
): RecyclerView.ViewHolder(binding.root){
private val tvInitials = binding.tvInitials
private val tvName = binding.tvName
private val tvOrdersIndicator = binding.tvOrdersIndicator
private val cardOperator = binding.cardOperator
fun bind(operator: Operator, colorFlag: Boolean) = with(itemView){
tvName.text = "${operator.name} ${operator.lastName} ${operator.mothersName}"
tvOrdersIndicator.text = "${operator.pendingOrders} / ${operator.totalOrders}"
tvInitials.text = HelperUtil().getInitials(
operator.name,
operator.lastName
)
if (operator.pendingOrders == 0L) cardOperator.setCardBackgroundColor(
itemView.context.resources.getColor(
R.color.lightDarkGray
)
) else cardOperator.setCardBackgroundColor(itemView.context.resources.getColor(android.R.color.white))
if (colorFlag) tvInitials.setBackgroundResource(R.drawable.circle_border_accent) else tvInitials.setBackgroundResource(
R.drawable.circle_border_primary
)
itemView.setOnClickListener { listener.onOperatorClicked(operator) }
}
}
}

View File

@@ -0,0 +1,201 @@
package com.iesoluciones.siodrenax.adapters
import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.Context.VIBRATOR_SERVICE
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.VibrationEffect
import android.os.Vibrator
import android.os.VibratorManager
import android.telephony.PhoneNumberUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.recyclerview.widget.RecyclerView
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.activities.OrderProgressActivity
import com.iesoluciones.siodrenax.databinding.ViewholderOrderBinding
import com.iesoluciones.siodrenax.entities.Order
import com.iesoluciones.siodrenax.entities.OrderProgress
import com.iesoluciones.siodrenax.repositories.OrdersRepository
import com.iesoluciones.siodrenax.utils.Constants.Companion.ORDER_ID
import com.iesoluciones.siodrenax.utils.StatusDrawable
import com.iesoluciones.siodrenax.utils.currencyFormat
import java.text.SimpleDateFormat
import java.util.*
class OrdersAdapter(
private val context: Context,
var orders: List<Order>
) : RecyclerView.Adapter<OrdersAdapter.ViewHolder>() {
companion object{
val inFormatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
val outFormatter = SimpleDateFormat("HH:mm", Locale.getDefault())
val ordersRepository: OrdersRepository = OrdersRepository()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding =
ViewholderOrderBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding, context)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(orders[position])
}
override fun getItemCount(): Int {
return orders.size
}
class ViewHolder(
val binding: ViewholderOrderBinding,
val context: Context
): RecyclerView.ViewHolder(binding.root) {
private val tvClientDenomination = binding.tvClientDenomination
private val tvOrderName = binding.tvOrderName
private val tvPayMethod = binding.tvPayMethod
private val tvContact = binding.tvContact
private val tvAddress = binding.tvAddress
private val tvServiceStatus = binding.tvServiceStatus
private val tvTitle = binding.tvTitle
private val tvCost = binding.tvCost
private val tvOrderId = binding.tvOrderId
private val tvOrderScheduledTime = binding.tvOrderScheduledTime
private val tvContactPhone = binding.tvContactPhone
private val linearContact = binding.linearContact
private val linearComments = binding.linearComments
private val tvComments = binding.tvComments
private val tvVehicle = binding.tvVehicle
fun bind(order: Order) = with(itemView){
try {
// NullPointerException launched once. The "dateServiceRequest" is a non null field.
// The WS sends the field and the Local Data Base saves it properly
// Commented to track the error -------------> Incidents = 1
val temp: Date = inFormatter.parse(order.dateServiceRequest)!!
tvClientDenomination.text = order.clientDenomination
tvOrderScheduledTime.text = context.resources.getString(
R.string.scheduled_time, outFormatter.format(
temp
)
)
} catch (e: Exception) {
e.printStackTrace()
tvClientDenomination.text = order.clientContactName
tvOrderScheduledTime.text = context.resources.getString(R.string.scheduled_time_error)
}
tvOrderName.text = order.serviceName
val orderProgress: OrderProgress? = ordersRepository.getOrderProgressById(order.id)
tvServiceStatus.text = if(orderProgress != null) context.getString(R.string.sincronizando) else order.serviceStatusName
tvVehicle.text = order.vehicleCodeName
if (order.getClientContactCellphone.trim().isNotEmpty()) {
tvContact.text = order.clientContactName
tvContactPhone.text = context.resources.getString(
R.string.contact_phone, PhoneNumberUtils.formatNumber(
order.getClientContactCellphone,
"MX"
)
)
tvContactPhone.visibility = View.VISIBLE
} else {
tvContact.text = order.clientContactName
tvContactPhone.visibility = View.GONE
}
tvPayMethod.text = order.payMethodName
val drawable = StatusDrawable(order.serviceStatusColor1, order.serviceStatusColor2)
tvTitle.background = drawable
tvOrderId.text = "${order.idRequest}"
tvCost.text = context.resources.getString(R.string.cost, order.cost.currencyFormat())
tvAddress.text = context.resources.getString(
R.string.address,
order.streetAddress,
order.extNumberAddress,
order.neighborhoodNameAddress,
order.clientPostalCodeAddress
)
if (order.comments != null) {
linearComments.visibility = View.VISIBLE
tvComments.text = order.comments
} else linearComments.visibility = View.GONE
linearContact.setOnLongClickListener {
if (order.getClientContactCellphone.trim().isNotEmpty()) {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions((itemView.context as AppCompatActivity), arrayOf(Manifest.permission.CALL_PHONE), 3)
return@setOnLongClickListener true
}else{
val callIntent = Intent(Intent.ACTION_CALL)
callIntent.data = Uri.parse("tel:" + order.getClientContactCellphone.trim()) // change the number
vibrate()
context.startActivity(callIntent)
}
}
false
}
linearContact.setOnClickListener {
//Se agrega por error en Crashlytics
val orderRepo: Order? = ordersRepository.getOrderById(order.id)
//Se agrega por error en Crashlytics
if (orderRepo != null) {
changeToOrderProgressActivity(order.id)
} else {
Toast.makeText(context, "No se encontró la información.", Toast.LENGTH_SHORT).show()
}
}
itemView.setOnClickListener {
//Se agrega por error en Crashlytics
val orderRepo: Order? = ordersRepository.getOrderById(order.id)
//Se agrega por error en Crashlytics
if (orderRepo != null) {
changeToOrderProgressActivity(order.id)
} else {
Toast.makeText(context, "No se encontró la información.", Toast.LENGTH_SHORT).show()
}
}
}
private fun changeToOrderProgressActivity(orderId: Long){
val i = Intent(context, OrderProgressActivity::class.java)
i.putExtra(ORDER_ID, orderId)
context.startActivity(i)
(context as AppCompatActivity).overridePendingTransition(
R.anim.slide_in_rigth_airbnb,
R.anim.scale_out_airbnb
)
}
@SuppressLint("MissingPermission")
private fun vibrate() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val vibratorManager = context.getSystemService(AppCompatActivity.VIBRATOR_MANAGER_SERVICE) as VibratorManager
vibratorManager.defaultVibrator.vibrate(VibrationEffect.createOneShot(150, VibrationEffect.DEFAULT_AMPLITUDE))
}else{
(context.getSystemService(VIBRATOR_SERVICE) as Vibrator).vibrate(VibrationEffect.createOneShot(150, VibrationEffect.DEFAULT_AMPLITUDE))
}
}
}
}

View File

@@ -0,0 +1,144 @@
package com.iesoluciones.siodrenax.adapters
import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.VibrationEffect
import android.os.Vibrator
import android.os.VibratorManager
import android.telephony.PhoneNumberUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.recyclerview.widget.RecyclerView
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.databinding.ViewholderOrderBinding
import com.iesoluciones.siodrenax.entities.Order
import com.iesoluciones.siodrenax.utils.StatusDrawable
import com.iesoluciones.siodrenax.utils.currencyFormat
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.*
class OrdersManagerAdapter(
private val context: Context,
var orders: List<Order>
) : RecyclerView.Adapter<OrdersManagerAdapter.ViewHolder>() {
companion object{
var inFormatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
var outFormatter = SimpleDateFormat("HH:mm", Locale.getDefault())
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding =
ViewholderOrderBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding, context)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(orders[position])
}
override fun getItemCount(): Int {
return orders.size
}
class ViewHolder(
val binding: ViewholderOrderBinding,
val context: Context
): RecyclerView.ViewHolder(binding.root) {
fun bind(order: Order){
try {
val temp: Date = inFormatter.parse(order.dateServiceRequest)!!
binding.tvClientDenomination.text = order.clientDenomination
binding.tvOrderScheduledTime.text = context.resources.getString(
R.string.scheduled_time, outFormatter.format(
temp
)
)
} catch (e: ParseException) {
e.printStackTrace()
binding.tvClientDenomination.text = order.clientContactName
binding.tvOrderScheduledTime.text = context.resources.getString(R.string.scheduled_time_error)
}
binding.tvOrderName.text = order.serviceName
binding.tvServiceStatus.text = order.serviceStatusName
if (order.getClientContactCellphone.trim().isNotEmpty()) {
binding.tvContact.text = order.clientContactName
binding.tvContactPhone.text = context.resources.getString(
R.string.contact_phone, PhoneNumberUtils.formatNumber(
order.getClientContactCellphone,
"MX"
)
)
binding.tvContactPhone.visibility = View.VISIBLE
} else {
binding.tvContact.text = order.clientContactName
binding.tvContactPhone.visibility = View.GONE
}
binding.tvPayMethod.text = order.payMethodName
val drawable = StatusDrawable(order.serviceStatusColor1, order.serviceStatusColor2)
binding.tvTitle.background = drawable
binding.tvVehicle.text = order.vehicleCodeName
binding.tvOrderId.text = "${order.idRequest}"
binding.tvCost.text = context.resources.getString(
R.string.cost,
order.cost.currencyFormat()
)
binding.tvAddress.text = context.resources.getString(
R.string.address,
order.streetAddress,
order.extNumberAddress,
order.neighborhoodNameAddress,
order.clientPostalCodeAddress
)
if (order.comments != null) {
binding.linearComments.visibility = View.VISIBLE
binding.tvComments.text = order.comments
} else binding.linearComments.visibility = View.GONE
binding.linearContact.setOnLongClickListener {
if (order.getClientContactCellphone.trim().isNotEmpty()) {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions((itemView.context as AppCompatActivity), arrayOf(Manifest.permission.CALL_PHONE), 3)
return@setOnLongClickListener true
}else{
val callIntent = Intent(Intent.ACTION_CALL)
callIntent.data = Uri.parse("tel:" + order.getClientContactCellphone.trim()) // change the number
vibrate()
context.startActivity(callIntent)
}
}
false
}
}
@SuppressLint("MissingPermission")
private fun vibrate() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val vibratorManager = context.getSystemService(AppCompatActivity.VIBRATOR_MANAGER_SERVICE) as VibratorManager
vibratorManager.defaultVibrator.vibrate(VibrationEffect.createOneShot(150, VibrationEffect.DEFAULT_AMPLITUDE))
}else{
(context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator).vibrate(VibrationEffect.createOneShot(150, VibrationEffect.DEFAULT_AMPLITUDE))
}
}
}
}

View File

@@ -0,0 +1,59 @@
package com.iesoluciones.siodrenax.adapters
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.recyclerview.widget.RecyclerView
import com.iesoluciones.siodrenax.databinding.ViewholderPdfBinding
class PdfViewerAdapter(
private val context: Context,
var pages: List<Bitmap>
): RecyclerView.Adapter<PdfViewerAdapter.ViewHolder>() {
class ViewHolder(
val binding: ViewholderPdfBinding,
val context: Context
): RecyclerView.ViewHolder(binding.root), View.OnTouchListener {
@SuppressLint("ClickableViewAccessibility")
fun bind(bitmap: Bitmap) = with(itemView){
val imageView: ImageView = binding.imageView
imageView.setImageBitmap(bitmap)
imageView.setOnTouchListener(this@ViewHolder)
}
override fun onTouch(view: View?, motionEvent: MotionEvent?): Boolean {
if(motionEvent!!.pointerCount >= 2 || (view!!.canScrollHorizontally(1) && view.canScrollHorizontally(-1))){
when (motionEvent.action){
MotionEvent.ACTION_MOVE -> {
view!!.parent.requestDisallowInterceptTouchEvent(true)
}
MotionEvent.ACTION_UP -> {
view!!.parent.requestDisallowInterceptTouchEvent(false)
}
else -> {return true}
}
}
return true
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = ViewholderPdfBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding, context)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(pages[position])
}
override fun getItemCount(): Int {
return pages.size
}
}

View File

@@ -0,0 +1,185 @@
package com.iesoluciones.siodrenax.adapters
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.View.OnFocusChangeListener
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Button
import androidx.recyclerview.widget.RecyclerView
import com.iesoluciones.siodrenax.App.Companion.context
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.checkListQuestionBox
import com.iesoluciones.siodrenax.databinding.ViewholderRevisionBinding
import com.iesoluciones.siodrenax.entities.CheckListQuestion
import com.iesoluciones.siodrenax.entities.CheckListQuestion_
import com.iesoluciones.siodrenax.repositories.CheckListRepository
import com.iesoluciones.siodrenax.utils.Constants
import io.objectbox.android.AndroidScheduler
import io.objectbox.kotlin.equal
import io.objectbox.reactive.DataSubscription
class RevisionSurveyAdapter(
private val btnSiguiente: Button,
private var checkListQuestionList: List<CheckListQuestion>,
private val vehicle: List<String>,
private val vehicleIds: List<Long>
) : RecyclerView.Adapter<RevisionSurveyAdapter.ViewHolder>() {
companion object {
var vehicleSelectedId: Long = 0
}
var checkListQuestionBoxObserve: DataSubscription? = null
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
checkListQuestionBoxObserve = checkListQuestionBox.query(
CheckListQuestion_.tipo equal Constants.REVISION
)
.build()
.subscribe()
.on(AndroidScheduler.mainThread())
.observer { checkListQuestion -> checkListQuestionList = checkListQuestion }
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding =
ViewholderRevisionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding, vehicle, vehicleIds)
}
override fun onBindViewHolder(holder: ViewHolder, i: Int) {
holder.bind(btnSiguiente, checkListQuestionList[i])
}
override fun getItemCount() = checkListQuestionList.size
class ViewHolder(
var binding: ViewholderRevisionBinding,
private val vehicle: List<String>,
val vehicleIds: List<Long>
) : RecyclerView.ViewHolder(binding.root) {
var isSpinnerTouched = false
fun bind(btnSiguiente: Button, checkListQuestion: CheckListQuestion) = with(itemView) {
binding.radioGroup.visibility =
if (checkListQuestion.tipoRadioBtn == 1) View.VISIBLE else View.GONE
binding.eTComentario.visibility =
if (checkListQuestion.tipoText == 1) View.VISIBLE else View.GONE
binding.spVehiculos.visibility =
if (checkListQuestion.id == 1L) View.VISIBLE else View.GONE
val isTipoSpinner =
checkListQuestion.tipoCheckBox == 0 && checkListQuestion.tipoRadioBtn == 0 && checkListQuestion.tipoText == 0
if (isTipoSpinner) {
val dataAdapter: ArrayAdapter<String> = ArrayAdapter<String>(
context,
android.R.layout.simple_spinner_item,
vehicle
)
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
binding.spVehiculos.adapter = dataAdapter
if (checkListQuestion.respuestaText != "") {
for (i in vehicle.indices) {
if (vehicle[i] == checkListQuestion.respuestaText) {
binding.spVehiculos.setSelection(i)
}
}
}
}
itemView.tag = checkListQuestion.id
binding.tvQuestionTitle.text = checkListQuestion.nombre
binding.eTComentario.setText(checkListQuestion.respuestaText)
val radioCheck: String? = checkListQuestion.respuestaRadioBtn
if (radioCheck != null && itemView.tag == checkListQuestion.id) {
binding.radioNormal.tag = checkListQuestion.id
binding.radioBajo.tag = checkListQuestion.id
if (radioCheck == context.getString(R.string.normal)) {
binding.radioGroup.check(R.id.radioNormal)
} else if (radioCheck == context.getString(R.string.bajo)) {
binding.radioGroup.check(R.id.radioBajo)
}
}
listeners(isTipoSpinner, btnSiguiente)
}
@SuppressLint("ClickableViewAccessibility")
private fun listeners(isTipoSpinner: Boolean, btnSiguiente: Button) {
val id = itemView.tag.toString().toLong()
val objCheck: CheckListQuestion = checkListQuestionBox.get(id)
if(objCheck.tipoText == 1){
binding.eTComentario.onFocusChangeListener = OnFocusChangeListener { _, hasFocus ->
if (!hasFocus) {
objCheck.respuestaText = (binding.eTComentario.text.toString())
checkListQuestionBox.put(objCheck)
}
}
}
if(objCheck.tipoRadioBtn == 1){
binding.radioBajo.setOnClickListener {
setCheckListText(objCheck, btnSiguiente, context!!.getString(R.string.bajo))
}
binding.radioNormal.setOnClickListener {
setCheckListText(objCheck, btnSiguiente, context!!.getString(R.string.normal))
}
}
if (isTipoSpinner) {
btnSiguiente.visibility = if (CheckListRepository().isValidRevision() && binding.spVehiculos.selectedItemPosition != 0) View.VISIBLE else View.GONE
binding.spVehiculos.setOnTouchListener { _, _ ->
isSpinnerTouched = true
false
}
binding.spVehiculos.onItemSelectedListener =
object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>,
view: View,
position: Int,
id: Long
) {
vehicleSelectedId = vehicleIds[position]
if (isSpinnerTouched) {
val item = parent.getItemAtPosition(position).toString()
objCheck.respuestaText = (if (position != 0) item else "")
checkListQuestionBox.put(objCheck)
isSpinnerTouched = false
btnSiguiente.visibility = if (CheckListRepository().isValidRevision() && vehicleSelectedId != 0L) View.VISIBLE else View.GONE
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
}
}
private fun setCheckListText(objCheck:CheckListQuestion, btnSiguiente:Button, text:String){
objCheck.respuestaRadioBtn = text
checkListQuestionBox.put(objCheck)
btnSiguiente.visibility = if (CheckListRepository().isValidRevision() && vehicleSelectedId != 0L) View.VISIBLE else View.GONE
}
}
override fun onViewRecycled(holder: ViewHolder) {
super.onViewRecycled(holder)
holder.binding.radioGroup.setOnCheckedChangeListener(null)
holder.binding.spVehiculos.onItemSelectedListener = null
holder.binding.radioGroup.clearCheck()
}
}

View File

@@ -0,0 +1,54 @@
package com.iesoluciones.siodrenax.entities
import com.google.gson.annotations.SerializedName
import com.iesoluciones.siodrenax.models.Answer
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
import io.objectbox.annotation.NameInDb
@Entity
data class BusinessAnswer(
@Id(assignable = true)
private var id: Long,
@NameInDb("pregunta_id")
@SerializedName("pregunta_id")
private var idQuestion: Long,
@NameInDb("nombre")
@SerializedName("nombre")
private var title: String,
@NameInDb("orden")
@SerializedName("orden")
private var order: Int,
@NameInDb("tipo_campo")
@SerializedName("tipo_campo")
private var type: String,
): Answer() {
override fun getId(): Long {
return id
}
override fun getIdQuestion(): Long {
return idQuestion
}
override fun getTitle(): String {
return title
}
override fun getOrder(): Int {
return order
}
override fun getType(): String {
return type
}
fun setId(id: Long){
this.id = id
}
}

View File

@@ -0,0 +1,54 @@
package com.iesoluciones.siodrenax.entities
import com.google.gson.annotations.SerializedName
import com.iesoluciones.siodrenax.models.Question
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
import io.objectbox.annotation.NameInDb
@Entity
data class BusinessQuestion(
@Id(assignable = true)
private var id: Long,
@NameInDb("nombre")
@SerializedName("nombre")
private var title: String,
@NameInDb("orden")
@SerializedName("orden")
private var order: Int,
@NameInDb("mostrar_numero")
@SerializedName("mostrar_numero")
private var showNumber: Int,
@NameInDb("obligatorio")
@SerializedName("obligatorio")
private var required: Boolean,
): Question() {
override fun getId(): Long {
return id
}
override fun getTitle(): String {
return title
}
override fun getOrder(): Int {
return order
}
override fun getShowNumber(): Int {
return showNumber
}
override fun getRequired(): Boolean {
return required
}
fun setId(id: Long){
this.id = id
}
}

View File

@@ -0,0 +1,51 @@
package com.iesoluciones.siodrenax.entities
import com.google.gson.annotations.SerializedName
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
import io.objectbox.annotation.NameInDb
import javax.annotation.Nullable
@Entity
data class CheckListQuestion(
@Id(assignable = true)
var id: Long,
var nombre: String,
@NameInDb("tipo_radio_btn")
@SerializedName("tipo_radio_btn")
var tipoRadioBtn: Int,
@NameInDb("tipo_text")
@SerializedName("tipo_text")
var tipoText: Int,
@NameInDb("tipo_checkbox")
@SerializedName("tipo_checkbox")
var tipoCheckBox: Int,
@NameInDb("tipo")
@SerializedName("tipo")
var tipo: String,
@Nullable
@NameInDb("respuesta_radio_btn")
@SerializedName("respuesta_radio_btn")
var respuestaRadioBtn: String? = null,
@Nullable
@NameInDb("respuesta_text")
@SerializedName("respuesta_text")
var respuestaText: String? = null,
@Nullable
@NameInDb("respuesta_checkbox")
@SerializedName("respuesta_checkbox")
var respuestaCheckBox: Boolean = false,
@Nullable
@NameInDb("fecha")
@SerializedName("fecha")
var fecha: String
)

View File

@@ -0,0 +1,52 @@
package com.iesoluciones.siodrenax.entities
import com.google.gson.annotations.SerializedName
import com.iesoluciones.siodrenax.models.Answer
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
import io.objectbox.annotation.NameInDb
@Entity
data class DomesticAnswer(
@Id(assignable = true)
private var id: Long,
@NameInDb("pregunta_id")
@SerializedName("pregunta_id")
private var idQuestion: Long,
@NameInDb("nombre")
@SerializedName("nombre")
private var title: String,
@NameInDb("orden")
@SerializedName("orden")
private var order: Int,
@NameInDb("tipo_campo")
@SerializedName("tipo_campo")
private var type: String,
): Answer() {
override fun getId(): Long {
return id
}
override fun getIdQuestion(): Long {
return idQuestion
}
override fun getTitle(): String {
return title
}
override fun getOrder(): Int {
return order
}
override fun getType(): String {
return type
}
fun setId(id: Long){
this.id = id
}
}

View File

@@ -0,0 +1,54 @@
package com.iesoluciones.siodrenax.entities
import com.google.gson.annotations.SerializedName
import com.iesoluciones.siodrenax.models.Question
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
import io.objectbox.annotation.NameInDb
@Entity
class DomesticQuestion(
@Id(assignable = true)
private var id: Long,
@NameInDb("nombre")
@SerializedName("nombre")
private var title: String,
@NameInDb("orden")
@SerializedName("orden")
private var order: Int,
@NameInDb("mostrar_numero")
@SerializedName("mostrar_numero")
private var showNumber: Int,
@NameInDb("obligatorio")
@SerializedName("obligatorio")
private var required: Boolean,
):Question() {
override fun getId(): Long {
return id
}
override fun getTitle(): String {
return title
}
override fun getOrder(): Int {
return order
}
override fun getShowNumber(): Int {
return showNumber
}
override fun getRequired(): Boolean {
return required
}
fun setId(id: Long){
this.id = id
}
}

View File

@@ -0,0 +1,48 @@
package com.iesoluciones.siodrenax.entities
import com.iesoluciones.siodrenax.utils.Constants.Companion.DEFAULT_LATITUDE
import com.iesoluciones.siodrenax.utils.Constants.Companion.DEFAULT_LONGITUDE
import com.iesoluciones.siodrenax.utils.Constants.Companion.EVIDENCE_FINAL
import com.iesoluciones.siodrenax.utils.Constants.Companion.EVIDENCE_PROCESS
import com.iesoluciones.siodrenax.utils.Constants.Companion.EVIDENCE_START
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
@Entity
data class Evidence(
@Id
var id: Long,
var evidenceNo: Long,
var idOrder: Long,
var idRequest: Long,
var type: Int,
var path: String?,
var name: String?,
var lat: String,
var lng: String,
var viewRef: Int,
var isSent: Boolean
){
constructor() : this(
0,
0,
0,
0,
0,
null,
null,
DEFAULT_LATITUDE.toString(),
DEFAULT_LONGITUDE.toString(),
0,
false
)
fun getTypeDescription(): String {
return when (this.type) {
EVIDENCE_START -> "Inicio"
EVIDENCE_PROCESS -> "Proceso"
EVIDENCE_FINAL -> "Final"
else -> ""
}
}
}

View File

@@ -0,0 +1,29 @@
package com.iesoluciones.siodrenax.entities
import com.iesoluciones.siodrenax.models.Reason
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
@Entity
data class NegativeServiceReason(
@Id(assignable = true)
private var id: Long,
private var descripcion: String
) : Reason() {
override fun getId(): Long {
return id
}
override fun getTitle(): String {
return descripcion
}
fun setId(id: Long){
this.id = id
}
fun getDescripcion(): String{
return descripcion
}
}

View File

@@ -0,0 +1,167 @@
package com.iesoluciones.siodrenax.entities
import com.google.gson.annotations.SerializedName
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
@Entity
data class NextDayOrder (
@Id(assignable = true)
@SerializedName("id")
var id: Long,
@SerializedName("servicio_id")
var idService: Long,
@SerializedName("solicitud_servicio_id")
var idRequest: Long,
@SerializedName("servicio_nombre")
var serviceName: String,
@SerializedName("estatus_servicio_id")
var idServiceStatus: Long,
@SerializedName("estatus_servicio_nombre")
var serviceStatusName: String,
@SerializedName("estatus_servicio_color_1")
var serviceStatusColor1: String,
@SerializedName("estatus_servicio_color_2")
var serviceStatusColor2: String? = null,
@SerializedName("forma_pago_id")
var idPayMethod: Long,
@SerializedName("forma_pago_nombre")
var payMethodName: String,
@SerializedName("tipo_servicio_id")
var idServiceType: Long,
@SerializedName("observacion_atencion_cliente")
var comments: String?,
@SerializedName("costo_servicio")
var cost: String,
@SerializedName("tipo_servicio_nombre")
var serviceTypeName: String,
@SerializedName("fecha_agenda")
var dateScheduled: String,
@SerializedName("fecha_solicitud")
var dateServiceRequest: String,
@SerializedName("usuario_agenda_id")
var idUserSchedule: Long,
@SerializedName("usuario_agenda_nombre")
var userScheduleName: String,
@SerializedName("usuario_agenda_apellido_paterno")
var userScheduleLastName: String,
@SerializedName("usuario_agenda_apellido_materno")
var getUserScheduleMothersLastName: String,
@SerializedName("duracion")
var duracion: String,
@SerializedName("definido_cliente")
var clientDefined: Long,
@SerializedName("cliente_id")
var idClient: Long,
@SerializedName("denominacion")
var clientDenomination: String,
@SerializedName("clientes_nombre_responsable_sucursal")
var clientContactName: String,
@SerializedName("clientes_celular_responsable")
var getClientContactCellphone: String,
@SerializedName("cliente_domicilio_id")
var idClientAddress: Long,
@SerializedName("clientes_calle")
var streetAddress: String? = null,
@SerializedName("clientes_num_ext")
var extNumberAddress: String? = null,
@SerializedName("clientes_num_int")
var intNumberAddress: String? = null,
@SerializedName("clientes_colonia")
var neighborhoodNameAddress: String? = null,
@SerializedName("clientes_cp")
var clientPostalCodeAddress: String? = null,
@SerializedName("clientes_telefono")
var clientPhone: String,
@SerializedName("clientes_lat")
var clientLat: String,
@SerializedName("clientes_lng")
var clientLng: String,
@SerializedName("operador_id")
var idOperator: Long,
@SerializedName("operador_nombre")
var operatorName: String,
@SerializedName("operador_apellido_paterno")
var operatorLastName: String,
@SerializedName("operador_apellido_materno")
var operatorMothersName: String,
@SerializedName("vehiculo_id")
var idVehicle: Long,
@SerializedName("vehiculo_num_economico")
var vehicleCodeName: String,
@SerializedName("vehiculo_sucursal")
var vehicleOfficeName: String,
@SerializedName("vehiculo_sucursal_id")
var idVehicleOffice: Long,
@SerializedName("operador_sucursal")
var operatorOfficeName: String,
@SerializedName("operador_sucursal_id")
var idOperatorOffice: Long,
@SerializedName("auxiliar_1_id")
var idAssistant1: Long,
@SerializedName("auxiliar_2_id")
var idAssistant2: Long,
@SerializedName("auxiliar_1")
var assistant1Name: String? = null,
@SerializedName("auxiliar_2")
var assistant2Name: String? = null,
@SerializedName("sucursal_auxiliar_1")
var assistant1OfficeName: String? = null,
@SerializedName("sucursal_auxiliar_2")
var assistant2OfficeName: String? = null,
@SerializedName("requiere_encuesta")
var SurveyRequired: Int,
var isSent: Boolean,
)

View File

@@ -0,0 +1,31 @@
package com.iesoluciones.siodrenax.entities
import com.google.gson.annotations.SerializedName
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
import io.objectbox.annotation.NameInDb
@Entity
data class Operator(
@Id(assignable = true) var id: Long,
@NameInDb("nombre")
@SerializedName("nombre")
var name: String,
@NameInDb("apellido_paterno")
@SerializedName("apellido_paterno")
var lastName: String,
@NameInDb("apellido_materno")
@SerializedName("apellido_materno")
var mothersName: String,
@NameInDb("servicios_total")
@SerializedName("servicios_total")
var totalOrders: Long,
@NameInDb("servicios_pendiente")
@SerializedName("servicios_pendiente")
var pendingOrders: Long
)

View File

@@ -0,0 +1,190 @@
package com.iesoluciones.siodrenax.entities
import com.google.gson.annotations.SerializedName
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
@Entity
data class Order (
@Id(assignable = true)
@SerializedName("id")
var id: Long,
@SerializedName("servicio_id")
var idService: Long,
@SerializedName("solicitud_servicio_id")
var idRequest: Long,
@SerializedName("servicio_nombre")
var serviceName: String,
@SerializedName("estatus_servicio_id")
var idServiceStatus: Long,
@SerializedName("estatus_servicio_nombre")
var serviceStatusName: String,
@SerializedName("estatus_servicio_color_1")
var serviceStatusColor1: String,
@SerializedName("estatus_servicio_color_2")
var serviceStatusColor2: String? = null,
@SerializedName("forma_pago_id")
var idPayMethod: Long,
@SerializedName("forma_pago_nombre")
var payMethodName: String,
@SerializedName("tipo_servicio_id")
var idServiceType: Long,
@SerializedName("observacion_atencion_cliente")
var comments: String?,
@SerializedName("costo_servicio")
var cost: String,
@SerializedName("costo_servicio_negativo")
var negativeCost: String,
@SerializedName("tipo_servicio_nombre")
var serviceTypeName: String,
@SerializedName("fecha_agenda")
var dateScheduled: String,
@SerializedName("fecha_solicitud")
var dateServiceRequest: String,
@SerializedName("usuario_agenda_id")
var idUserSchedule: Long,
@SerializedName("usuario_agenda_nombre")
var userScheduleName: String,
@SerializedName("usuario_agenda_apellido_paterno")
var userScheduleLastName: String,
@SerializedName("usuario_agenda_apellido_materno")
var getUserScheduleMothersLastName: String,
@SerializedName("duracion")
var duracion: String,
@SerializedName("definido_cliente")
var clientDefined: Long,
@SerializedName("cliente_id")
var idClient: Long,
@SerializedName("denominacion")
var clientDenomination: String,
@SerializedName("clientes_nombre_responsable_sucursal")
var clientContactName: String,
@SerializedName("clientes_celular_responsable")
var getClientContactCellphone: String,
@SerializedName("cliente_domicilio_id")
var idClientAddress: Long,
@SerializedName("clientes_calle")
var streetAddress: String? = null,
@SerializedName("clientes_num_ext")
var extNumberAddress: String? = null,
@SerializedName("clientes_num_int")
var intNumberAddress: String? = null,
@SerializedName("clientes_colonia")
var neighborhoodNameAddress: String? = null,
@SerializedName("clientes_cp")
var clientPostalCodeAddress: String? = null,
@SerializedName("clientes_ciudad")
var clientCity: String,
@SerializedName("clientes_telefono")
var clientPhone: String,
@SerializedName("clientes_lat")
var clientLat: String,
@SerializedName("clientes_lng")
var clientLng: String,
@SerializedName("operador_id")
var idOperator: Long,
@SerializedName("operador_nombre")
var operatorName: String,
@SerializedName("operador_apellido_paterno")
var operatorLastName: String,
@SerializedName("operador_apellido_materno")
var operatorMothersName: String,
@SerializedName("vehiculo_id")
var idVehicle: Long,
@SerializedName("vehiculo_num_economico")
var vehicleCodeName: String,
@SerializedName("vehiculo_sucursal")
var vehicleOfficeName: String,
@SerializedName("vehiculo_sucursal_id")
var idVehicleOffice: Long,
@SerializedName("operador_sucursal")
var operatorOfficeName: String,
@SerializedName("operador_sucursal_id")
var idOperatorOffice: Long,
@SerializedName("auxiliar_1_id")
var idAssistant1: Long,
@SerializedName("auxiliar_2_id")
var idAssistant2: Long,
@SerializedName("auxiliar_1")
var assistant1Name: String? = null,
@SerializedName("auxiliar_1_apellido_paterno")
var assistant1LastName: String? = null,
@SerializedName("auxiliar_1_apellido_materno")
var assistant1MothersName: String? = null,
@SerializedName("auxiliar_2")
var assistant2Name: String? = null,
@SerializedName("sucursal_auxiliar_1")
var assistant1OfficeName: String? = null,
@SerializedName("sucursal_auxiliar_2")
var assistant2OfficeName: String? = null,
@SerializedName("requiere_encuesta")
var SurveyRequired: Int,
var isSent: Boolean,
@SerializedName("pdf_path")
var pdfPath: String?,
@SerializedName("nombre_croquis")
var sketchName: String?,
var sketchPath: String?,
@SerializedName("nombre_sucursal")
var branchName: String,
)

View File

@@ -0,0 +1,54 @@
package com.iesoluciones.siodrenax.entities
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
@Entity
data class OrderProgress (
@Id(assignable = true)
var id: Long,
var idRequest: Long,
var startDate: String,
var endDate: String?,
var comments: String?,
var elapsedTime: String?,
var startLat: String,
var startLng: String,
var endLat: String,
var endLng: String,
var signaturePath: String?,
var signatureName: String?,
var warranty: Boolean,
var isSent: Boolean,
var shouldBeSent: Boolean,
var signatureSent: Boolean,
var isOnSurvey: Boolean,
var shouldSendSurvey: Boolean,
var surveySent: Boolean,
var liters: Int,
var negativeService: Int,
){
constructor() : this(0,
0,
"",
null,
"",
null,
"",
"",
"",
"",
null,
null,
false,
false,
false,
false,
false,
false,
false,
0,
0
)
}

View File

@@ -0,0 +1,21 @@
package com.iesoluciones.siodrenax.entities
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
@Entity
data class SavedAnswer(
@Id(assignable = true)
var id: Long,
var questionId: Long,
var orderId: Long,
var isOpen: Boolean,
var answer: String?
){
constructor() : this(0,
0,
0,
false,
null
)
}

View File

@@ -0,0 +1,32 @@
package com.iesoluciones.siodrenax.entities
import com.google.gson.annotations.SerializedName
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
import io.objectbox.annotation.NameInDb
@Entity
data class User(
@Id(assignable = true) var id: Long,
var email: String,
@NameInDb("nombre")
@SerializedName("nombre")
var username: String,
@NameInDb("apellido_paterno")
@SerializedName("apellido_paterno")
var lastName: String,
@NameInDb("apellido_materno")
@SerializedName("apellido_materno")
var mothersName: String,
@NameInDb("telefono")
@SerializedName("telefono")
var phone: String,
@NameInDb("tipo_empleado_id")
@SerializedName("tipo_empleado_id")
var idEmployeeType: Long
)

View File

@@ -0,0 +1,10 @@
package com.iesoluciones.siodrenax.entities
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
@Entity
data class Vehicle(
@Id(assignable = true) var id: Long,
var nombre: String
)

View File

@@ -0,0 +1,82 @@
package com.iesoluciones.siodrenax.fragments.dialogs
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.databinding.FragmentIncidentsInputBinding
import com.iesoluciones.siodrenax.interfaces.OnIncidentListener
class IncidentsInputFragment : DialogFragment(){
private lateinit var _binding: FragmentIncidentsInputBinding
private val binding get() = _binding
lateinit var listener: OnIncidentListener
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentIncidentsInputBinding.inflate(inflater, container, false)
binding.tvContinue.setOnClickListener { onClickTvContinue() }
binding.tvCancel.setOnClickListener { onClickTvCancel() }
return binding.root
}
private fun onClickTvContinue() {
val editIncident = binding.editIncident
if (isEmpty(editIncident)) {
binding.labelIncident.error = getText(R.string.incident_dialog_error)
listener.onIncidentInput(null)
} else {
listener.onIncidentInput(editIncident.text.toString().replace(",".toRegex(), ""))
dismiss()
}
}
private fun onClickTvCancel() {
listener.onNoIncident()
dismiss()
}
override fun show(manager: FragmentManager, tag: String?) {
try {
val ft = manager.beginTransaction()
ft.add(this, tag)
ft.commitAllowingStateLoss()
} catch (e: IllegalStateException) {
Log.e("Exception", e.toString())
}
}
override fun onAttach(context: Context) {
super.onAttach(context)
listener = if (activity is OnIncidentListener) {
(activity as OnIncidentListener?)!!
} else throw RuntimeException("Parent Activity not implementing OnIncidentListener")
}
private fun isEmpty(incident: EditText): Boolean {
return incident.text.toString().trim { it <= ' ' }.isEmpty() || incident.text == null
}
companion object {
fun newInstance(listener: OnIncidentListener): IncidentsInputFragment {
val fragment = IncidentsInputFragment()
fragment.listener = listener
return fragment
}
}
}

View File

@@ -0,0 +1,86 @@
package com.iesoluciones.siodrenax.fragments.dialogs
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.databinding.FragmentMileageInputDialogBinding
import com.iesoluciones.siodrenax.interfaces.OnMileageListener
import com.iesoluciones.siodrenax.utils.NumberFormatterTextWatcher
class MileageInputDialogFragment : DialogFragment() {
private lateinit var _binding: FragmentMileageInputDialogBinding
private val binding get() = _binding
lateinit var listener: OnMileageListener
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentMileageInputDialogBinding.inflate(inflater, container, false)
val view = binding.root
isCancelable = false
binding.labelMileage.suffixText = getText(R.string.mileage_dialog_suffix)
binding.tvContinue.setOnClickListener {
if (isEmpty(binding.editMileage)) {
binding.labelMileage.error = getText(R.string.mileage_dialog_error)
listener.onMileageInput(null)
} else {
listener.onMileageInput(
binding.editMileage.text.toString().replace(",".toRegex(), "")
)
dismiss()
}
}
binding.tvCancel.setOnClickListener {
dismiss()
}
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.labelMileage.suffixText = getText(R.string.mileage_dialog_suffix)
binding.editMileage.addTextChangedListener(NumberFormatterTextWatcher(binding.editMileage))
}
override fun show(manager: FragmentManager, tag: String?) {
try {
val ft = manager.beginTransaction()
ft.add(this, tag)
ft.commitAllowingStateLoss()
} catch (e: IllegalStateException) {
Log.e("Exception", e.toString())
}
}
override fun onAttach(context: Context) {
super.onAttach(context)
listener = if (activity is OnMileageListener) {
activity as OnMileageListener
} else throw RuntimeException("Parent Activity not implementing OnMileageListener")
}
private fun isEmpty(mileage: EditText?): Boolean {
return mileage!!.text.toString().trim { it <= ' ' }.isEmpty() || mileage.text == null
}
companion object {
fun newInstance(listener: OnMileageListener): MileageInputDialogFragment {
val fragment = MileageInputDialogFragment()
fragment.listener = listener
return fragment
}
}
}

View File

@@ -0,0 +1,66 @@
package com.iesoluciones.siodrenax.fragments.dialogs
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import com.iesoluciones.siodrenax.databinding.FragmentVehicleIncidentsBinding
import com.iesoluciones.siodrenax.interfaces.OnIncidentShowListener
class VehicleIncidentsFragment : DialogFragment() {
private lateinit var _binding: FragmentVehicleIncidentsBinding
private val binding get() = _binding
lateinit var listener: OnIncidentShowListener
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FragmentVehicleIncidentsBinding.inflate(inflater, container, false)
val view = binding.root
binding.tvIncidence.text = incidence
isCancelable = false
binding.tvContinue.setOnClickListener{
listener.onIncidenceSolved()
dismiss()
}
binding.tvCancel.setOnClickListener{
listener.onIncidenceNotSolved()
dismiss()
}
return view
}
override fun show(manager: FragmentManager, tag: String?) {
try {
val ft = manager.beginTransaction()
ft.add(this, tag)
ft.commitAllowingStateLoss()
} catch (e: IllegalStateException) {
Log.e("Exception", e.toString())
}
}
override fun onAttach(context: Context) {
super.onAttach(context)
listener = if (activity is OnIncidentShowListener) {
activity as OnIncidentShowListener
} else throw RuntimeException("Parent Activity not implementing OnIncidentListener")
}
companion object {
var incidence: String? = null
fun newInstance(listener: OnIncidentShowListener, incidence: String?): VehicleIncidentsFragment {
val fragment = VehicleIncidentsFragment()
fragment.listener = listener
Companion.incidence = incidence
return fragment
}
}
}

View File

@@ -0,0 +1,6 @@
package com.iesoluciones.siodrenax.interfaces
interface OnIncidentListener {
fun onNoIncident()
fun onIncidentInput(incident: String?)
}

View File

@@ -0,0 +1,6 @@
package com.iesoluciones.siodrenax.interfaces
interface OnIncidentShowListener {
fun onIncidenceSolved()
fun onIncidenceNotSolved()
}

View File

@@ -0,0 +1,5 @@
package com.iesoluciones.siodrenax.interfaces
interface OnMileageListener {
fun onMileageInput(mileage: String?)
}

View File

@@ -0,0 +1,10 @@
package com.iesoluciones.siodrenax.models
abstract class Answer {
abstract fun getId(): Long
abstract fun getIdQuestion(): Long
abstract fun getTitle(): String
abstract fun getOrder(): Int
abstract fun getType(): String
}

View File

@@ -0,0 +1,30 @@
package com.iesoluciones.siodrenax.models
import com.google.gson.annotations.SerializedName
import com.iesoluciones.siodrenax.entities.BusinessAnswer
import com.iesoluciones.siodrenax.entities.DomesticAnswer
data class AnswerPOJO(
@SerializedName("id")
var id: Long,
@SerializedName("pregunta_id")
var idQuestion: Long,
@SerializedName("nombre")
var title: String,
@SerializedName("orden")
var order: Int,
@SerializedName("tipo_campo")
var type: String,
){
fun getDomesticAnswer(): DomesticAnswer {
return DomesticAnswer(id, idQuestion, title, order, type)
}
fun getBusinessAnswer(): BusinessAnswer {
return BusinessAnswer(id, idQuestion, title, order, type)
}
}

View File

@@ -0,0 +1,11 @@
package com.iesoluciones.siodrenax.models
import com.google.gson.annotations.SerializedName
data class CheckBoxAnswer (
@SerializedName("pregunta_id")
var questionId: String,
@SerializedName("respuesta_id")
var answerId: String?
)

View File

@@ -0,0 +1,18 @@
package com.iesoluciones.siodrenax.models
import com.google.gson.annotations.SerializedName
data class EvidenceRequest(
@SerializedName("nombre")
var name: String,
@SerializedName("etapa")
var stage: String,
@SerializedName("lat")
var lat: String,
@SerializedName("lng")
var lng: String,
)

View File

@@ -0,0 +1,6 @@
package com.iesoluciones.siodrenax.models
data class EvidenceSignatureLocal(
var path: String?,
var name: String?,
)

View File

@@ -0,0 +1,44 @@
package com.iesoluciones.siodrenax.models
import com.google.gson.annotations.SerializedName
import javax.annotation.Nullable
data class FinishOrderRequest(
@SerializedName("servicio_det_id")
var idOrder: String,
@SerializedName("servicio_enc_id")
var idRequest: String,
@SerializedName("fecha_fin_celular")
var endDate: String,
@SerializedName("lat_fin")
var endLat: String,
@SerializedName("lng_fin")
var endLng: String,
@SerializedName("duracion")
var elapsedTime: String,
@SerializedName("comentarios")
var comments: String,
@SerializedName("aplica_garantia")
var warranty: Int,
@SerializedName("litraje")
var liters: Int,
@SerializedName("cat_motivos_estatus_id")
var negativeService: Int,
@Nullable
@SerializedName("encuesta")
var survey: List<Any>?,
@SerializedName("recibo")
var receipt: String,
)

View File

@@ -0,0 +1,8 @@
package com.iesoluciones.siodrenax.models
import com.google.gson.annotations.SerializedName
data class GenericResponse (
@SerializedName("result")
internal var response: String
)

View File

@@ -0,0 +1,18 @@
package com.iesoluciones.siodrenax.models
import com.google.gson.annotations.SerializedName
data class KeyValue (
@SerializedName("llave")
var key: String,
@SerializedName("valor")
var value: String
){
override fun toString(): String {
return "KeyValue{" +
"key='" + key + '\'' +
", value='" + value + '\'' +
'}'
}
}

View File

@@ -0,0 +1,23 @@
package com.iesoluciones.siodrenax.models
import com.google.gson.annotations.SerializedName
import com.iesoluciones.siodrenax.entities.User
data class LoginResponse (
@SerializedName("user")
internal var user: User,
@SerializedName("token")
internal var token: String,
@SerializedName("parametros")
internal var parmams: List<KeyValue>,
) {
override fun toString(): String {
return "LoginResponse{" +
"user=" + user +
", token='" + token + '\'' +
", parmams=" + parmams +
'}'
}
}

View File

@@ -0,0 +1,11 @@
package com.iesoluciones.siodrenax.models
import com.google.gson.annotations.SerializedName
data class OpenAnswer (
@SerializedName("pregunta_id")
var questionId: String,
@SerializedName("respuesta")
var answer: String?
)

View File

@@ -0,0 +1,25 @@
package com.iesoluciones.siodrenax.models
data class OrderPdf(
var requestId: Long,
var client: String,
var date: String,
var address: String,
var city: String,
var cellphone: String,
var contactName: String,
var service: String,
var observations: String,
var warrantyApplied: String,
var cost: String,
var negativeCost: String,
var startTime: String,
var elapsedTime: String,
var finishTime: String,
var advisor: String,
var branchOffice: String,
var sign: String,
var auxiliar: String,
var vehicle: String,
var evidences: MutableMap<String, ArrayList<String>>
)

View File

@@ -0,0 +1,9 @@
package com.iesoluciones.siodrenax.models
abstract class Question {
abstract fun getId(): Long
abstract fun getTitle(): String
abstract fun getOrder(): Int
abstract fun getShowNumber(): Int
abstract fun getRequired(): Boolean
}

View File

@@ -0,0 +1,33 @@
package com.iesoluciones.siodrenax.models
import com.google.gson.annotations.SerializedName
import com.iesoluciones.siodrenax.entities.BusinessQuestion
import com.iesoluciones.siodrenax.entities.DomesticQuestion
data class QuestionPOJO(
@SerializedName("id")
var id: Long,
@SerializedName("nombre")
var title: String,
@SerializedName("orden")
var order: Int,
@SerializedName("mostrar_numero")
var showNumber: Int,
@SerializedName("obligatorio")
var required: Int,
@SerializedName("respuestas")
var answers: List<AnswerPOJO>
){
fun getDomesticQuestion(): DomesticQuestion {
return DomesticQuestion(id, title, order, showNumber, (required == 1))
}
fun getBusinessQuestion(): BusinessQuestion {
return BusinessQuestion(id, title, order, showNumber, (required == 1))
}
}

View File

@@ -0,0 +1,6 @@
package com.iesoluciones.siodrenax.models
abstract class Reason {
abstract fun getId(): Long
abstract fun getTitle(): String
}

View File

@@ -0,0 +1,47 @@
package com.iesoluciones.siodrenax.models
import com.google.gson.annotations.SerializedName
import com.iesoluciones.siodrenax.entities.BusinessQuestion
import com.iesoluciones.siodrenax.entities.DomesticQuestion
import com.iesoluciones.siodrenax.entities.NegativeServiceReason
import com.iesoluciones.siodrenax.entities.Order
import java.util.*
data class StartWorkdayResponse(
@SerializedName("servicios")
var orders: List<Order>,
@SerializedName("parametros")
var keyValues: List<KeyValue>,
@SerializedName("jornada")
var workdayInfo: Workday,
@SerializedName("encuesta_domestico")
var domesticSurvey: List<QuestionPOJO>,
@SerializedName("encuesta_empresarial")
var businessSurvey: List<QuestionPOJO>,
@SerializedName("motivos_estatus")
var negativeServiceReason: List<NegativeServiceReason>
){
fun parseDomesticSurvey(): List<DomesticQuestion> {
val survey: MutableList<DomesticQuestion> = ArrayList<DomesticQuestion>()
for (q in domesticSurvey) {
val question: DomesticQuestion = q.getDomesticQuestion()
survey.add(question)
}
return survey
}
fun parseBussinesSurvey(): List<BusinessQuestion> {
val survey: MutableList<BusinessQuestion> = ArrayList<BusinessQuestion>()
for (q in businessSurvey) {
val question: BusinessQuestion = q.getBusinessQuestion()
survey.add(question)
}
return survey
}
}

View File

@@ -0,0 +1,5 @@
package com.iesoluciones.siodrenax.models
data class ValidationOrderAnswer(
var servicio_id: String
)

View File

@@ -0,0 +1,18 @@
package com.iesoluciones.siodrenax.models
import com.google.gson.annotations.SerializedName
data class VehicleIncidenceResponse (
@SerializedName("id")
var id: Int,
@SerializedName("descripcion")
var description: String
){
override fun toString(): String{
return "Incidence {" +
"id =" + id +
", descripcion = " + description +
'}'
}
}

View File

@@ -0,0 +1,26 @@
package com.iesoluciones.siodrenax.models
import com.google.gson.annotations.SerializedName
data class Workday (
@SerializedName("kilometraje_inicial")
var startOdometer: String,
@SerializedName("lat_ini")
var initLat: String,
@SerializedName("lng_ini")
var initLng: String,
@SerializedName("vehiculo_id")
var idVehicle: Long,
@SerializedName("usuario_id")
var idUser: Long,
@SerializedName("fecha_hora_ini")
var initDate: String,
@SerializedName("id")
var idWorkload: Long,
)

View File

@@ -0,0 +1,118 @@
package com.iesoluciones.siodrenax.network
import com.iesoluciones.siodrenax.entities.*
import com.iesoluciones.siodrenax.models.*
import io.reactivex.Observable
import okhttp3.ResponseBody
import retrofit2.http.*
import java.util.*
interface Api {
@FormUrlEncoded
@POST("login")
fun login(
@Header("Application") appId: String,
@Field("email") email: String,
@Field("password") password: String,
@Field("version_apk") version: String,
@Field("dispositivo_id") deviceId: String
): Observable<LoginResponse>
@GET("operador/checklist")
fun getCheckList(
@Query("cambiar_vehiculo") changeVehicle: Int
): Observable<List<CheckListQuestion>>
@GET("operador/checklist/vehiculos")
fun getVehicle(): Observable<List<Vehicle>>
@FormUrlEncoded
@POST("operador/vehiculos_incidencias")
fun sendVehicleIncidence(
@Field("descripcion") incidence: String?
): Observable<GenericResponse>
@FormUrlEncoded
@POST("operador/vehiculos_incidencias/ultima_incidencia")
fun getVehicleIncidence(
@Field("vehiculo_id") vehicleId: Long
): Observable<VehicleIncidenceResponse>
@PUT("operador/vehiculos_incidencias/{incidencia_id}/resolver")
fun resolveVehicleIncidence(
@Path("incidencia_id") incidenceId: String
): Observable<GenericResponse>
@POST("operador/checklist")
fun sendCheckList(
@Body checkListQuestions: List<CheckListQuestion>
): Observable<ResponseBody>
@FormUrlEncoded
@POST("operador/iniciarjornada")
fun startWorkday(
@Field("token_firebase") firebaseToken: String,
@Field("kilometraje_inicial") odometer: String,
@Field("lat_ini") initLat: String,
@Field("lng_ini") initLng: String,
@Field("modelo_celular") phoneModel: String,
@Field("bateria") batteryLevel: String
): Observable<StartWorkdayResponse>
@FormUrlEncoded
@POST("operador/finalizarjornada/{bitacora_id}")
fun endWorkday(
@Path("bitacora_id") idWorkday: String,
@Field("kilometraje_final") odometer: String,
@Field("lat_fin") lastLat: String,
@Field("lng_fin") lastLng: String,
@Field("modelo_celular") phoneModel: String,
@Field("bateria") batteryLevel: String
): Observable<ResponseBody>
@GET("operador/solicitud_servicios")
fun getOrders(): Observable<List<Order>>
@GET("operador/solicitud_servicios/{idOrder}")
fun getOrderInfo(
@Path("idOrder") idWorkday: String
): Observable<Order>
@FormUrlEncoded
@POST("operador/solicitud_servicios/iniciar")
fun startOrder(
@Field("servicio_det_id") idOrder: String,
@Field("servicio_enc_id") idRequest: String,
@Field("fecha_ini_celular") startDate: String,
@Field("lat_ini") startLat: String,
@Field("lng_ini") startLng: String
): Observable<ResponseBody>
@POST("operador/solicitud_servicios/verificar/servicios")
fun verificarServicio(@Body servicios_id: ArrayList<Long>): Observable<List<ValidationOrderAnswer>>
@POST("operador/solicitud_servicios/finalizar-new")
fun finishOrder(
@Body finishOrderRequest: FinishOrderRequest,
): Observable<ResponseBody>
@GET("supervisoroperaciones/asesores")
fun listOperators(): Observable<List<Operator>>
@GET("supervisoroperaciones/asesores/{asesor_id}/servicios")
fun getOperatorOrders(
@Path("asesor_id") idOperator: String
): Observable<List<Order>>
@POST("supervisoroperaciones/iniciarjornada")
fun startWorkdayManager(): Observable<Workday>
@POST("supervisoroperaciones/finalizarjornada/{bitacora_id}")
fun endWorkdayManager(
@Path("bitacora_id") workdayId: String
): Observable<ResponseBody>
@GET("operador/servicios/diasiguiente")
fun getNextDayOrders(): Observable<List<NextDayOrder>>
}

View File

@@ -0,0 +1,35 @@
package com.iesoluciones.siodrenax.network
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import retrofit2.HttpException
import java.io.IOException
internal class HttpError(json: String) {
@SerializedName("error")
var error: String? = null
init {
val gson = Gson()
val temp = gson.fromJson(json, HttpError::class.java)
error = temp.error
}
companion object {
fun parseException(exception: HttpException): HttpError? {
return try {
HttpError(exception.response().errorBody()!!.source().readUtf8())
} catch (e: IOException) {
e.printStackTrace()
null
}
}
}
override fun toString(): String {
return "{error : $error}"
}
}

View File

@@ -0,0 +1,166 @@
package com.iesoluciones.siodrenax.network
import com.iesoluciones.siodrenax.App
import com.iesoluciones.siodrenax.BuildConfig.BASE_URL
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.preferencesHelper
import com.iesoluciones.siodrenax.userBox
import okhttp3.*
import okhttp3.logging.HttpLoggingInterceptor
import okio.Buffer
import org.json.JSONException
import org.json.JSONObject
import java.io.IOException
import java.util.concurrent.TimeUnit
class TokenInterceptor : Interceptor {
private var client: OkHttpClient? = null
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val original = chain.request()
val builder = original.newBuilder()
.addHeader("Content-Type", "application/json")
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Bearer " + preferencesHelper.tokenApi)
val request = builder.build()
val response: Response = chain.proceed(request)
//Instantiate Logging interceptor
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BODY
//Let's finish last workday started before starting a new one
//Instantiate Http client to use
client = OkHttpClient.Builder()
.addInterceptor(TokenInterceptor())
.addInterceptor(logging)
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.build()
when (response.code()) {
401 -> {
val body1 = response.body()!!.source().readUtf8()
val error = HttpError(body1)
if (error.error != null) {
if (error.error.toString().equals(
App.shareInstance!!.resources.getString(R.string.token_expired),
true
)
) {
val request1 = Request.Builder()
.url(BASE_URL + "refresh")
.build()
val response1 = client!!.newCall(request1).execute()
try {
val json = JSONObject(response1.body()!!.source().readUtf8())
preferencesHelper.tokenApi = json.getString("token")
val original1 = chain.request()
val builder1 = original1.newBuilder()
val request2 = builder1.build()
return client!!.newCall(request2).execute()
} catch (e: JSONException) {
e.printStackTrace()
}
} else
return response.newBuilder()
.body(ResponseBody.create(response.body()!!.contentType(), body1))
.code(401).build()
} else
return response.newBuilder()
.body(ResponseBody.create(response.body()!!.contentType(), body1))
.code(401).build()
}
420 -> {
val body1 = response.body()!!.source().readUtf8()
return response.newBuilder()
.body(ResponseBody.create(response.body()!!.contentType(), body1))
.code(420).build()
}
422 -> {
val body1 = response.body()!!.source().readUtf8()
val error1 = UnprocessableEntity(body1)
if (error1.message.contentEquals(App.shareInstance!!.resources.getString(R.string.workday_already_started))) {
//Extract the request body, the same data of the body will be used to finish pending workday
var requestBody = request.body()
//Modify some keys to match the one requested by the web service
requestBody = processFormDataRequestBody(requestBody!!)
val response1: Response
if (userBox.query().build().findUnique()!!.idEmployeeType == 2L) {
//Set up request
val request1 = Request.Builder()
.url(
BASE_URL + "operador/finalizarjornada/" + error1.errors[0]
) //concatenate id of last workday
.post(requestBody!!)
.build()
//Call response
response1 = client!!.newCall(request1).execute()
} else {
//Set up request
val request1 = Request.Builder()
.url(
BASE_URL + "supervisoroperaciones/finalizarjornada/" + error1.errors[0]
) //concatenate id of last workday
.post(requestBody!!)
.build()
//Call response
response1 = client!!.newCall(request1).execute()
}
//Validate succesful workday finish
return if (response1.code() == 200) {
//try again startworkday
client!!.newCall(chain.request()).execute()
} else {
//There was something wrong with the process , just pass the error to main Thread
val body = response1.body()!!.source().readUtf8()
response.newBuilder()
.body(ResponseBody.create(response.body()!!.contentType(), body))
.code(422).build()
}
} else {
return response.newBuilder()
.body(ResponseBody.create(response.body()!!.contentType(), body1))
.code(422).build()
}
}
else -> return response
}
return response
}
private fun bodyToString(request: RequestBody?): String {
try {
val buffer = Buffer()
if (request != null)
request.writeTo(buffer)
else
return ""
return buffer.readUtf8()
} catch (e: IOException) {
return "did not work"
}
}
private fun processFormDataRequestBody(requestBody: RequestBody): RequestBody? {
val formBody: RequestBody = FormBody.Builder().build()
var postBodyString = bodyToString(requestBody)
postBodyString += (if (postBodyString.isNotEmpty()) "&" else "") + bodyToString(formBody)
postBodyString = postBodyString.replace("kilometraje_inicial", "kilometraje_final")
postBodyString = postBodyString.replace("lat_ini", "lat_fin")
postBodyString = postBodyString.replace("lng_ini", "lng_fin")
return RequestBody.create(requestBody.contentType(), postBodyString)
}
}

View File

@@ -0,0 +1,43 @@
package com.iesoluciones.siodrenax.network
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import retrofit2.HttpException
import java.io.IOException
class UnprocessableEntity(json: String) {
@SerializedName("message")
var message: String
internal set
@SerializedName("errors")
var errors: List<String>
internal set
init {
val gson = Gson()
val temp = gson.fromJson(json, UnprocessableEntity::class.java)
message = temp.message
errors = temp.errors
}
companion object {
fun parseException(exception: HttpException): UnprocessableEntity? {
return try {
UnprocessableEntity(exception.response().errorBody()!!.source().readUtf8().toString())
} catch (e: IOException) {
e.printStackTrace()
null
}
}
}
override fun toString(): String {
return "UnprocessableEntity{" +
"message='" + message + '\''.toString() +
", errors=" + errors +
'}'.toString()
}
}

View File

@@ -0,0 +1,34 @@
package com.iesoluciones.siodrenax.receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import com.iesoluciones.siodrenax.services.NotificationService.Companion.UPDATE_UI
class UpdateUIReceiver: BroadcastReceiver() {
private var listener: UiUpdateListener? = null
interface UiUpdateListener {
fun updateUI()
}
fun updateUIReceiver(listener: UiUpdateListener) {
this.listener = listener
(listener as Context).registerReceiver(this, IntentFilter(UPDATE_UI))
}
fun unRegisterReceiver(listener: UiUpdateListener) {
(listener as Context).unregisterReceiver(this)
}
fun registerReceiver(listener: UiUpdateListener) {
this.listener = listener
(listener as Context).registerReceiver(this, IntentFilter(UPDATE_UI))
}
override fun onReceive(context: Context?, intent: Intent?) {
if (listener != null) listener!!.updateUI()
}
}

View File

@@ -0,0 +1,76 @@
package com.iesoluciones.siodrenax.repositories
import com.iesoluciones.siodrenax.api
import com.iesoluciones.siodrenax.checkListQuestionBox
import com.iesoluciones.siodrenax.entities.CheckListQuestion
import com.iesoluciones.siodrenax.entities.CheckListQuestion_
import com.iesoluciones.siodrenax.utils.Constants
import io.objectbox.kotlin.and
import io.objectbox.kotlin.equal
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.observers.ResourceObserver
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
class CheckListRepository {
interface CheckListInterface {
fun success()
fun failure(throwable: Throwable)
}
fun isCheckListDone(): Boolean {
return checkListQuestionBox.query()
.build()
.find().isNotEmpty()
}
fun getSurveyCheckList(tipo: String): List<CheckListQuestion> {
return checkListQuestionBox.query(
CheckListQuestion_.tipo equal tipo
)
.build()
.find()
}
fun isValidRevision(): Boolean {
val totalRadio = checkListQuestionBox.query(
CheckListQuestion_.tipo equal Constants.REVISION
and (CheckListQuestion_.tipoRadioBtn equal 1)
and (CheckListQuestion_.respuestaRadioBtn.isNull)
)
.build()
.count()
val totalSelect = checkListQuestionBox.query(
CheckListQuestion_.tipo equal Constants.REVISION
and (CheckListQuestion_.tipoCheckBox equal 0)
and (CheckListQuestion_.tipoRadioBtn equal 0)
and (CheckListQuestion_.tipoText equal 0)
and (CheckListQuestion_.respuestaText.isNull)
)
.build()
.count()
return (totalRadio + totalSelect) == 0L
}
fun finalizarEncuesta(checkListInterface: CheckListInterface) {
api.sendCheckList(checkListQuestionBox.all)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map { response ->
checkListQuestionBox.removeAll()
response
}
.subscribe(object : ResourceObserver<ResponseBody>() {
override fun onNext(responseBody: ResponseBody) {}
override fun onError(e: Throwable) = checkListInterface.failure(e)
override fun onComplete() = checkListInterface.success()
})
}
}

View File

@@ -0,0 +1,15 @@
package com.iesoluciones.siodrenax.repositories
import com.iesoluciones.siodrenax.entities.Evidence
import com.iesoluciones.siodrenax.evidenceBox
class EvidenceRepository {
fun getEvidenceById(idEvidence: Long): Evidence {
return evidenceBox.get(idEvidence)
}
fun saveEvidence(evidence: Evidence) {
evidenceBox.put(evidence)
}
}

View File

@@ -0,0 +1,91 @@
package com.iesoluciones.siodrenax.repositories
import com.iesoluciones.siodrenax.api
import com.iesoluciones.siodrenax.models.GenericResponse
import com.iesoluciones.siodrenax.models.VehicleIncidenceResponse
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.observers.ResourceObserver
import io.reactivex.schedulers.Schedulers
class IncidenceRepository {
interface IncidenceInterface {
fun success(genericResponse: String?)
fun failure(throwable: Throwable)
}
interface GetIncidenceInterface {
fun success(response: VehicleIncidenceResponse?)
fun failure(throwable: Throwable)
}
fun sendVehicleIncidenceRequest(incidence: String, incidenceInterface: IncidenceInterface) {
api.sendVehicleIncidence(incidence)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map { response ->
response
}
.subscribe(object : ResourceObserver<GenericResponse>() {
override fun onNext(s: GenericResponse) {
incidenceInterface.success(s.response)
}
override fun onError(e: Throwable) {
incidenceInterface.failure(e)
}
override fun onComplete() {
}
})
}
fun getVehicleIncidenceRequest(vehicleId: Long, incidenceInterface: GetIncidenceInterface) {
api.getVehicleIncidence(vehicleId)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map { response ->
response
}
.subscribe(object : ResourceObserver<VehicleIncidenceResponse>() {
override fun onNext(s: VehicleIncidenceResponse) {
incidenceInterface.success(s)
}
override fun onError(e: Throwable) {
incidenceInterface.failure(e)
}
override fun onComplete() {
}
})
}
fun resolveVehicleIncidenceRequest(incidenceId: String, incidenceInterface: IncidenceInterface) {
api.resolveVehicleIncidence(incidenceId)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map { response ->
response
}
.subscribe(object : ResourceObserver<GenericResponse>() {
override fun onNext(s: GenericResponse) {
incidenceInterface.success(s.response)
}
override fun onError(e: Throwable) {
incidenceInterface.failure(e)
}
override fun onComplete() {
}
})
}
}

View File

@@ -0,0 +1,62 @@
package com.iesoluciones.siodrenax.repositories
import com.iesoluciones.siodrenax.*
import com.iesoluciones.siodrenax.utils.Constants.Companion.APP_ID
import com.iesoluciones.siodrenax.utils.Constants.Companion.EMPLOYEE_MANAGER
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.observers.ResourceObserver
import io.reactivex.schedulers.Schedulers
class LoginRepository {
interface LoginInterface {
fun success(result: Boolean)
fun failure(throwable: Throwable)
}
fun loginRequest(username: String, password: String, deviceId: String, loginInterface: LoginInterface) {
val obLogin = api.login(APP_ID, username, password, BuildConfig.VERSION_NAME, deviceId)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map { response ->
preferencesHelper.tokenApi = response.token
val user = response.user
userBox.put(user)
preferencesHelper.isManager = user.idEmployeeType == EMPLOYEE_MANAGER
response
}
val obCheckList = api.getCheckList(0)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map { response ->
checkListQuestionBox.put(response)
response
}
val obVehicle = api.getVehicle()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map { response ->
vehicleBox.put(response)
response
}
Observable.concat(obLogin, obCheckList, obVehicle)
.subscribe(object : ResourceObserver<Any?>() {
override fun onNext(t: Any) {
}
override fun onError(e: Throwable) {
loginInterface.failure(e)
}
override fun onComplete() {
loginInterface.success(true)
}
})
}
}

View File

@@ -0,0 +1,121 @@
package com.iesoluciones.siodrenax.repositories
import com.iesoluciones.siodrenax.api
import com.iesoluciones.siodrenax.entities.Operator
import com.iesoluciones.siodrenax.entities.Order
import com.iesoluciones.siodrenax.entities.User
import com.iesoluciones.siodrenax.models.Workday
import com.iesoluciones.siodrenax.operatorBox
import com.iesoluciones.siodrenax.preferencesHelper
import com.iesoluciones.siodrenax.userBox
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.observers.ResourceObserver
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
class ManagerRepository {
interface OnOrdersRetrievedListener {
fun success(orders: List<Order>)
fun failure(throwable: Throwable)
}
interface OnOperatorsRetrievedListener {
fun success(operators: List<Operator>)
fun failure(throwable: Throwable)
}
interface OnWebServiceCalled {
fun success()
fun failure(e: Throwable)
}
fun getOperatorList(listener: OnOperatorsRetrievedListener){
api.listOperators()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.subscribe(object : ResourceObserver<List<Operator>>() {
override fun onNext(operators: List<Operator>) {
operatorBox.put(operators)
listener.success(operators)
}
override fun onError(e: Throwable) {
listener.failure(e)
}
override fun onComplete() {}
})
}
fun getOperatorOrders(idOperator: String, listener: OnOrdersRetrievedListener){
api.getOperatorOrders(idOperator)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.subscribe(object : ResourceObserver<List<Order>>() {
override fun onNext(orders: List<Order>) {
listener.success(orders)
}
override fun onError(e: Throwable) {
listener.failure(e)
}
override fun onComplete() {}
})
}
fun getOperatorsFromDb(): List<Operator> {
return operatorBox.all
}
fun logOut(){
operatorBox.removeAll()
userBox.removeAll()
preferencesHelper.tokenApi = null
preferencesHelper.tokenFirebase = null
preferencesHelper.isManager = false
preferencesHelper.workdayStarted = false
preferencesHelper.workdayId = 0
}
fun startWorkday(listener: OnWebServiceCalled){
api.startWorkdayManager()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.subscribe(object : ResourceObserver<Workday>() {
override fun onNext(workday: Workday) {
preferencesHelper.workdayId = workday.idWorkload.toInt()
listener.success()
}
override fun onError(e: Throwable) {
listener.failure(e)
}
override fun onComplete() {}
})
}
fun endWorkday(listener: OnWebServiceCalled){
api.endWorkdayManager(preferencesHelper.workdayId.toString())
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.subscribe(object : ResourceObserver<ResponseBody>() {
override fun onNext(responseBody: ResponseBody) {
logOut()
listener.success()
}
override fun onError(e: Throwable) {
listener.failure(e)
}
override fun onComplete() {}
})
}
fun getUserData(): User {
return userBox.all[0]
}
}

View File

@@ -0,0 +1,349 @@
package com.iesoluciones.siodrenax.repositories
import android.content.Intent
import com.iesoluciones.siodrenax.*
import com.iesoluciones.siodrenax.App.Companion.shareInstance
import com.iesoluciones.siodrenax.entities.*
import com.iesoluciones.siodrenax.models.EvidenceRequest
import com.iesoluciones.siodrenax.models.FinishOrderRequest
import com.iesoluciones.siodrenax.models.Question
import com.iesoluciones.siodrenax.models.ValidationOrderAnswer
import com.iesoluciones.siodrenax.services.NotificationService
import com.iesoluciones.siodrenax.utils.Constants
import com.iesoluciones.siodrenax.utils.HelperUtil
import io.objectbox.kotlin.equal
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.observers.ResourceObserver
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
class OrdersRepository {
interface OnRefreshOrders {
fun success(orders: List<Order>)
fun failure(throwable: Throwable)
}
interface OnRefreshONextDayOrders {
fun success(nextDayOrders: List<NextDayOrder>)
fun failure(throwable: Throwable)
}
interface OnOrderRequestAnswered {
fun success(orderResponse: ResponseBody)
fun failure(throwable: Throwable)
}
fun getOrders(): List<Order> {
return orderBox.query()
.order(Order_.dateServiceRequest)
.build()
.find()
}
fun getOrderById(id: Long): Order? {
return orderBox.query()
.equal(Order_.id, id)
.build()
.findFirst()
}
fun getOrderByIdRequest(idRequest: Long): Order? {
return orderBox.query()
.equal(Order_.idRequest, idRequest)
.build()
.findFirst()
}
fun getOrderProgressById(id: Long): OrderProgress? {
return orderProgressBox.query()
.equal(OrderProgress_.id, id)
.build()
.findFirst()
}
fun refreshOrders(listener: OnRefreshOrders, filesDir: String) {
api.getOrders()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.subscribe(object : ResourceObserver<List<Order>>() {
override fun onNext(orders: List<Order>) {
val ordersInBox: List<Order> =
orderBox.query().order(Order_.dateServiceRequest).build().find()
for (o in ordersInBox) {
val orderProgress: OrderProgress? = getOrderProgressById(o.id)
if (orderProgress == null || orderProgress.isSent) {
orderBox.remove(o.id)
}
}
orderBox.put(orders)
HelperUtil().downloadSketchs(filesDir,orders)
listener.success(getOrders())
}
override fun onError(e: Throwable) {
listener.failure(e)
}
override fun onComplete() {}
})
}
fun startOrder(
idOrder: String,
idRequest: String,
startDate: String,
startLat: String,
startLng: String,
listener: OnOrderRequestAnswered
) {
api.startOrder(idOrder, idRequest, startDate, startLat, startLng)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.subscribe(object : ResourceObserver<ResponseBody>() {
override fun onNext(responseBody: ResponseBody) {
HelperUtil().createEvidences(getOrderById(idOrder.toLong())!!)
val orderProgress = OrderProgress()
orderProgress.id = idOrder.toLong()
orderProgress.idRequest = idRequest.toLong()
orderProgress.startDate = startDate
orderProgress.startLat = startLat
orderProgress.startLng = startLng
orderProgressBox.put(orderProgress)
preferencesHelper.orderInProgress = idOrder.toLong()
listener.success(responseBody)
}
override fun onError(e: Throwable) {
listener.failure(e)
}
override fun onComplete() {}
})
}
fun createSurveyAnswerHolders(order: Order) {
val surveyRepository = SurveyRepository()
val questions: List<Question> =
if (order.idServiceType == Constants.ID_SERVICE_TYPE_DOMESTIC) {
surveyRepository.getSurveyQuestions(true)
} else {
surveyRepository.getSurveyQuestions(false)
}
for (q in questions) {
val savedAnswer = SavedAnswer()
savedAnswer.orderId = order.id
savedAnswer.questionId = q.getId()
savedAnswerBox.put(savedAnswer)
}
}
fun getEvidences(idOrder: Long): List<Evidence> {
return evidenceBox.query()
.equal(Evidence_.idOrder, idOrder)
.order(Evidence_.id)
.build()
.find(0, 9)
}
fun getEvidencesForZip(idOrder: Long): List<Evidence> {
return evidenceBox.query()
.equal(Evidence_.idOrder, idOrder)
.and()
.notNull(Evidence_.path)
.build()
.find()
}
fun getEvidencesForPdf(idOrder: Long): List<Evidence> {
return evidenceBox.query()
.equal(Evidence_.idOrder, idOrder)
.and()
.notNull(Evidence_.path)
.order(Evidence_.evidenceNo)
.build()
.find()
}
fun saveOrderProgress(orderProgress: OrderProgress) {
orderProgressBox.put(orderProgress)
}
fun getPendingOrders(): List<OrderProgress> {
//System.gc()
val ordersIdList = orderProgressBox.query()
.equal(OrderProgress_.shouldBeSent, true)
.and().equal(OrderProgress_.isSent, false)
.build()
.findIds()
validateOrdersSuccess(ordersIdList.toCollection(ArrayList()))
return orderProgressBox.query()
.equal(OrderProgress_.shouldBeSent, true)
.and().equal(OrderProgress_.isSent, false)
.build()
.find()
}
private fun validateOrdersSuccess(ordersIdList: ArrayList<Long>) {
api.verificarServicio(ordersIdList)
.subscribeOn(Schedulers.trampoline())
.observeOn(Schedulers.trampoline())
.subscribe(object : ResourceObserver<List<ValidationOrderAnswer>>() {
override fun onNext(validationOrderAnswers: List<ValidationOrderAnswer>) {
for (v in validationOrderAnswers) {
evidenceBox.query(
Evidence_.idOrder equal v.servicio_id
)
.build()
.remove()
orderProgressBox.remove(v.servicio_id.toLong())
orderBox.remove(v.servicio_id.toLong())
//TODO
// shareInstance!!.sendBroadcast(Intent(PushNotificationMessagingService.UPDATE_UI))
// shareInstance!!.startService(Intent(shareInstance, CleaningService::class.java))
preferencesHelper.orderInProgress = null
}
}
override fun onError(e: Throwable) {}
override fun onComplete() {}
})
}
private fun getPendingEvidences(idOrder: String): List<Evidence> {
return evidenceBox.query()
.equal(Evidence_.idOrder, idOrder.toLong())
.and().equal(Evidence_.isSent, false)
.and().notNull(Evidence_.path)
.build()
.find()
}
fun prepareFinishOrder(orderProgress: OrderProgress): Observable<ResponseBody> {
val order = getOrderById(orderProgress.id)!!
val answers: List<Any> = SurveyRepository().getSurveyBody(order)
val evidences = arrayListOf<EvidenceRequest>()
getEvidencesForZip(orderProgress.id).forEach {
evidences.add(EvidenceRequest(it.name!!, it.getTypeDescription(), it.lat, it.lng))
}
val finishOrderRequest = FinishOrderRequest(
idOrder = orderProgress.id.toString(),
idRequest = orderProgress.idRequest.toString(),
endDate = orderProgress.endDate!!,
endLat = orderProgress.endLat,
endLng = orderProgress.endLng,
elapsedTime = orderProgress.elapsedTime!!,
comments = orderProgress.comments!!,
warranty = if (orderProgress.warranty) 1 else 0,
liters = orderProgress.liters,
negativeService = orderProgress.negativeService,
survey = if (order.SurveyRequired == 1 && orderProgress.shouldSendSurvey) answers else null,
receipt = HelperUtil().encodeBase64(order.pdfPath!!)!!
)
return api.finishOrder(finishOrderRequest)
.doOnNext {
removeAllOrdersInformation(
orderProgress.id,
order.pdfPath!!,
orderProgress.signaturePath!!
)
shareInstance!!.sendBroadcast(Intent(NotificationService.UPDATE_UI))
preferencesHelper.orderInProgress = null
}
.doOnError {
orderProgress.isSent = false
orderProgressBox.put(orderProgress)
}
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
}
fun validateEvidences(idOrder: String): Boolean {
// Validar almenos una imagen de cada etapa
/*
val evidences = getPendingEvidences(idOrder)
var hasAtLeastOneStartEvicende = false
var hasAtLeastOneProcessEvicende = false
var hasAtLeastOneFinalEvicende = false
for (e in evidences) {
when (e.type) {
Constants.EVIDENCE_START -> hasAtLeastOneStartEvicende = true
Constants.EVIDENCE_PROCESS -> hasAtLeastOneProcessEvicende = true
Constants.EVIDENCE_FINAL -> hasAtLeastOneFinalEvicende = true
}
}
return hasAtLeastOneStartEvicende && hasAtLeastOneProcessEvicende && hasAtLeastOneFinalEvicende
*/
return getPendingEvidences(idOrder).size == 9
}
fun getNextDayOrders(): List<NextDayOrder> {
return nextDayOrderBox.query()
.order(NextDayOrder_.dateServiceRequest)
.build()
.find()
}
fun refreshNextDayOrders(listener: OnRefreshONextDayOrders) {
api.getNextDayOrders()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.subscribe(object : ResourceObserver<List<NextDayOrder>>() {
override fun onNext(orders: List<NextDayOrder>) {
nextDayOrderBox.removeAll()
nextDayOrderBox.put(orders)
listener.success(getNextDayOrders())
}
override fun onError(e: Throwable) {
listener.failure(e)
}
override fun onComplete() {}
})
}
private fun removeAllOrdersInformation(idOrder: Long, pdfPath: String, signaturePath: String) {
getEvidencesForZip(idOrder).forEach {
HelperUtil().deleteFile(it.path!!)
}
HelperUtil().deleteFile(pdfPath)
HelperUtil().deleteFile(signaturePath)
evidenceBox.query()
.equal(Evidence_.idOrder, idOrder)
.build()
.remove()
savedAnswerBox.query()
.equal(SavedAnswer_.orderId, idOrder)
.build()
.remove()
orderProgressBox.remove(idOrder)
orderBox.remove(idOrder)
}
}

View File

@@ -0,0 +1,104 @@
package com.iesoluciones.siodrenax.repositories
import android.util.Log
import com.iesoluciones.siodrenax.*
import com.iesoluciones.siodrenax.entities.*
import com.iesoluciones.siodrenax.models.*
import com.iesoluciones.siodrenax.utils.Constants.Companion.IDSERVICETYPEDOMESTIC
import java.util.*
class SurveyRepository {
fun getSurveyQuestions(isDomestic: Boolean): List<Question> {
val questions: MutableList<Question> = ArrayList<Question>()
if (isDomestic) {
questions.addAll(domesticQuestionBox.all)
} else {
questions.addAll(businessQuestionBox.all)
}
return questions
}
fun getSurveyBody(order: Order): List<Any> {
// NullPointerException launched once. The "Order" is a non null object.
// Commented to track the error -------------> Incidents = 12
val answerKeyValues: MutableList<Any> = ArrayList<Any>()
val questions =
getSurveyQuestions(order.idServiceType == IDSERVICETYPEDOMESTIC)
for (q in questions) {
val answer: SavedAnswer? = getSavedAnswer(q, order)
if (answer != null) {
if (answer.isOpen)
answerKeyValues.add(OpenAnswer(q.getId().toString(), answer.answer))
else
answerKeyValues.add(CheckBoxAnswer(q.getId().toString(), answer.answer))
}
}
return answerKeyValues
}
fun deleteAllSavedAnswers(order: Order) {
savedAnswerBox.query()
.equal(SavedAnswer_.orderId, order.id)
.build()
.remove()
}
private fun getSavedAnswer(question: Question, order: Order): SavedAnswer? {
return savedAnswerBox.query()
.equal(SavedAnswer_.orderId, order.id)
.and().equal(SavedAnswer_.questionId, question.getId())
.build()
.findFirst()
}
fun getNegativeServiceReason(): List<Reason> {
return negativeServiceReasonBox.all
}
fun validateAnswers(order: Order): Boolean {
val questions = getSurveyQuestions(order.idServiceType == IDSERVICETYPEDOMESTIC)
for (q in questions) {
val answer: String? = getQuestionSavedAnswer(q, order)
if (q.getRequired() && (answer == null || answer.trim { it <= ' ' } == ""))
return false
}
return true
}
fun getQuestionSavedAnswer(question: Question, order: Order): String? {
return getSavedAnswer(question, order)?.answer
}
fun getAnswers(q: Question, isDomestic: Boolean): List<Answer> {
return if (isDomestic) getDomesticQuestionAnswers(q) else getBusinessQuestionAnswers(q)
}
private fun getDomesticQuestionAnswers(q: Question): List<Answer> {
return domesticAnswerBox.query()
.equal(DomesticAnswer_.idQuestion, q.getId())
.build()
.find()
}
private fun getBusinessQuestionAnswers(q: Question): List<Answer> {
return businessAnswerBox.query()
.equal(BusinessAnswer_.idQuestion, q.getId())
.build()
.find()
}
fun saveQuestionAnswer(question: Question, order: Order, answer: String?, isOpen: Boolean) {
val savedAnswer = getSavedAnswer(question, order)
if (savedAnswer != null) {
savedAnswer.isOpen = isOpen
savedAnswer.answer = answer
savedAnswerBox.put(savedAnswer)
} else {
Log.e("SurveyRepository", "Trouble trying to save answer $answer")
}
}
}

View File

@@ -0,0 +1,251 @@
package com.iesoluciones.siodrenax.repositories
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build
import android.os.Build.VERSION_CODES
import androidx.core.app.NotificationManagerCompat
import com.iesoluciones.siodrenax.*
import com.iesoluciones.siodrenax.App.Companion.context
import com.iesoluciones.siodrenax.App.Companion.shareInstance
import com.iesoluciones.siodrenax.entities.OrderProgress_
import com.iesoluciones.siodrenax.entities.User
import com.iesoluciones.siodrenax.models.StartWorkdayResponse
import com.iesoluciones.siodrenax.utils.HelperUtil
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.observers.ResourceObserver
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
class WorkdayRepository {
interface WorkdayInterface {
fun success(response: StartWorkdayResponse)
fun failure(throwable: Throwable)
}
interface WorkdayEndInterface {
fun success(response: ResponseBody)
fun failure(throwable: Throwable)
}
fun startWorkdayRequest(
firebaseToken: String,
odometer: String,
initLat: String,
initLng: String,
filesDir: String,
listener: WorkdayInterface
) {
//obtain device model info
val reqString = (Build.MANUFACTURER
+ " " + Build.MODEL + " " + Build.VERSION.RELEASE
+ " " + VERSION_CODES::class.java.fields[Build.VERSION.SDK_INT].name)
//Obtain device battery percentage in integer
val ifilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
val batteryStatus: Intent = shareInstance!!.registerReceiver(null, ifilter)!!
val level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
val scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
val batteryPct = level / scale.toDouble() * 100.0
val battery = batteryPct.toInt()
api.startWorkday(
firebaseToken,
odometer,
initLat,
initLng,
reqString,
battery.toString() + ""
)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map { response ->
processWorkdayResponse(response, filesDir)
response
}
.subscribe(object : ResourceObserver<StartWorkdayResponse>() {
override fun onNext(response: StartWorkdayResponse) {
listener.success(response)
}
override fun onError(e: Throwable) {
listener.failure(e)
}
override fun onComplete() {
}
})
}
private fun processWorkdayResponse(response: StartWorkdayResponse, filesDir: String){
preferencesHelper.workdayStarted = true
preferencesHelper.workdayId = response.workdayInfo.idWorkload.toInt()
orderBox.put(response.orders)
domesticQuestionBox.put(response.parseDomesticSurvey())
businessQuestionBox.put(response.parseBussinesSurvey())
negativeServiceReasonBox.put(response.negativeServiceReason)
for (q in response.businessSurvey)
for (answerPOJO in q.answers)
businessAnswerBox.put(answerPOJO.getBusinessAnswer())
for (q in response.domesticSurvey)
for (answerPOJO in q.answers)
domesticAnswerBox.put(answerPOJO.getDomesticAnswer())
for (keyValue in response.keyValues)
if (keyValue.key == shareInstance!!.getString(R.string.gps_interval_param))
preferencesHelper.gpsInterval = keyValue.value.toInt()
HelperUtil().downloadSketchs(filesDir,response.orders)
}
fun endWorkdayRequest(
odometer: String,
endLat: String,
endLng: String,
listener: WorkdayEndInterface
){
val reqString = (Build.MANUFACTURER
+ " " + Build.MODEL + " " + Build.VERSION.RELEASE
+ " " + VERSION_CODES::class.java.fields[Build.VERSION.SDK_INT].name)
val ifilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
val batteryStatus: Intent = shareInstance!!.registerReceiver(null, ifilter)!!
val level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
val scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
val batteryPct = level / scale.toDouble() * 100.0
val battery = batteryPct.toInt()
api.endWorkday(
preferencesHelper.workdayId.toString(),
odometer,
endLat,
endLng,
reqString,
battery.toString()
)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map { response ->
preferencesHelper.workdayStarted = false
preferencesHelper.workdayId = 0
response
}
.subscribe(object : ResourceObserver<ResponseBody>() {
override fun onNext(response: ResponseBody) {
listener.success(response)
}
override fun onError(e: Throwable) {
listener.failure(e)
}
override fun onComplete() {
}
})
}
fun validateChangeVehicle(): Boolean {
return orderProgressBox.query()
.equal(OrderProgress_.isSent, false)
.build()
.find()
.isEmpty()
}
fun changeVehicle(
odometer: String,
endLat: String,
endLng: String,
listener: WorkdayEndInterface
) {
val reqString = (Build.MANUFACTURER
+ " " + Build.MODEL + " " + Build.VERSION.RELEASE
+ " " + VERSION_CODES::class.java.fields[Build.VERSION.SDK_INT].name)
val ifilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
val batteryStatus: Intent = shareInstance!!.registerReceiver(null, ifilter)!!
val level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
val scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
val batteryPct = level / scale.toDouble() * 100.0
val battery = batteryPct.toInt()
api.endWorkday(
preferencesHelper.workdayId.toString(),
odometer,
endLat,
endLng,
reqString,
battery.toString()
)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map { response ->
preferencesHelper.workdayStarted = false
preferencesHelper.workdayId = 0
response
}
.subscribe(object : ResourceObserver<ResponseBody>() {
override fun onNext(response: ResponseBody) {
listener.success(response)
}
override fun onError(e: Throwable) {
listener.failure(e)
}
override fun onComplete() {
}
})
}
fun getUser(): User {
return userBox.all[0]
}
fun logout() {
preferencesHelper.tokenApi = null
preferencesHelper.isEnableHerramientaSurvey = true
preferencesHelper.workdayStarted = false
preferencesHelper.orderInProgress = 0L
userBox.removeAll()
checkListQuestionBox.removeAll()
vehicleBox.removeAll()
orderBox.removeAll()
evidenceBox.removeAll()
businessQuestionBox.removeAll()
domesticQuestionBox.removeAll()
negativeServiceReasonBox.removeAll()
businessAnswerBox.removeAll()
domesticAnswerBox.removeAll()
savedAnswerBox.removeAll()
orderProgressBox.removeAll()
nextDayOrderBox.removeAll()
NotificationManagerCompat.from(context!!).cancelAll()
}
fun validateLogOut(): Boolean {
return orderProgressBox.query()
.equal(OrderProgress_.isSent, false)
.build()
.find()
.isEmpty()
}
fun validateRenewSession() : Boolean {
return orderBox.isEmpty
}
}

View File

@@ -0,0 +1,71 @@
package com.iesoluciones.siodrenax.services
import android.content.Intent
import android.os.Handler
import android.os.Looper
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.iesoluciones.siodrenax.R
import com.iesoluciones.siodrenax.api
import com.iesoluciones.siodrenax.entities.Order
import com.iesoluciones.siodrenax.orderBox
import com.iesoluciones.siodrenax.utils.HelperUtil
import com.iesoluciones.siodrenax.utils.NotificationUtils
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.observers.ResourceObserver
import io.reactivex.schedulers.Schedulers
class NotificationService: FirebaseMessagingService() {
companion object {
const val UPDATE_UI = "update_ui"
}
override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)
// -----------------------------------------------------------------------------
// ---------- Second way to launch notification (valid to foreground) ----------
val notification = remoteMessage.data
if(notification.containsKey("servicio_id")){
Handler(Looper.getMainLooper()).postDelayed({
api.getOrderInfo(notification["servicio_id"]!!)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.subscribe(object : ResourceObserver<Order>() {
override fun onNext(order: Order) {
if (order.id != 0L) {
orderBox.put(order)
HelperUtil().downloadSketchs(filesDir.absolutePath, mutableListOf(order))
sendBroadcast(Intent(UPDATE_UI))
NotificationUtils().showNotification(
resources.getString(R.string.app_name),
resources.getString(R.string.notification_accept_succes_message)
)
}
}
override fun onError(e: Throwable) {
NotificationUtils().showNotification(
resources.getString(R.string.app_name),
resources.getString(R.string.notification_accept_success_refresh_message)
)
}
override fun onComplete() {
}
})
}, 1000)
}
// -----------------------------------------------------------------------------
}
}

View File

@@ -0,0 +1,206 @@
package com.iesoluciones.siodrenax.utils;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.os.VibrationEffect;
import android.os.Vibrator;
import androidx.fragment.app.Fragment;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.animation.AccelerateDecelerateInterpolator;
import com.iesoluciones.siodrenax.R;
/**
* Creado por Informática Electoral Proceso Electoral 2017.
* Gustavo@ie-soluciones.com
*/
public class CircularProgressButton extends androidx.appcompat.widget.AppCompatButton {
public interface ProgressListener {
void onProgressStart();
void onProgressFinish();
}
private final CircularProgressDrawable mInactiveDrawable;
private final CircularProgressDrawable mActiveDrawable;
private CircularProgressDrawable mCurrentDrawable;
private final Vibrator mVibrator;
private ProgressListener mProgressListener;
private Animator currentAnimation;
private boolean mHoldingDown;
@SuppressWarnings("deprecation")
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public CircularProgressButton(Context context, AttributeSet attrs) {
super(context, attrs);
setTransformationMethod(null);
if (this.isInEditMode()) {
this.mInactiveDrawable = null;
this.mActiveDrawable = null;
this.mVibrator = null;
return;
}
this.mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
this.mActiveDrawable = this.mCurrentDrawable = new CircularProgressDrawable.Builder()
.setRingWidth(this.getResources().getDimensionPixelOffset(R.dimen.drawable_ring_size))
.setOutlineColor(this.getResources().getColor(R.color.colorAccent))
.setCenterColor(this.getResources().getColor(R.color.colorAccent))
.setRingColor(this.getResources().getColor(R.color.colorPrimaryDark))
.setFillColor(this.getResources().getColor(R.color.colorPrimaryDark))
.create();
this.mInactiveDrawable = new CircularProgressDrawable.Builder()
.setRingWidth(this.getResources().getDimensionPixelOffset(R.dimen.drawable_ring_size))
.setOutlineColor(this.getResources().getColor(R.color.colorAccent))
.setCenterColor(this.getResources().getColor(R.color.colorAccent))
.setRingColor(this.getResources().getColor(R.color.colorPrimaryDark))
.setFillColor(this.getResources().getColor(R.color.colorPrimaryDark))
.create();
this.post(() -> setBackground(mCurrentDrawable));
}
@Override
@SuppressWarnings("SuspiciousNameCombination")
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// set height as long as the width so the widget is perfectly circular
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
@Override
@SuppressLint({"ClickableViewAccessibility", "MissingPermission"})
public boolean onTouchEvent(MotionEvent event) {
if (this.mCurrentDrawable == this.mInactiveDrawable) {
// do no more
return true;
}
if (event.getAction() == MotionEvent.ACTION_DOWN) {
double distance = this.distanceFromCenter(event.getX(), event.getY());
if (distance < this.getWidth() / 2F) {
this.mVibrator.vibrate(VibrationEffect.createOneShot(25, 10));
this.mHoldingDown = true;
if (this.mProgressListener != null) {
this.mProgressListener.onProgressStart();
}
if (this.currentAnimation != null) {
this.currentAnimation.cancel();
}
this.currentAnimation = prepareStyle2Animation();
this.currentAnimation.start();
}
}
if (event.getAction() == MotionEvent.ACTION_UP) {
this.mHoldingDown = false;
if (this.currentAnimation != null) {
this.currentAnimation.cancel();
}
this.mCurrentDrawable.setProgress(0f);
this.mCurrentDrawable.setCircleScale(0f);
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
double distance = this.distanceFromCenter(event.getX(), event.getY());
//if (event.getX() < 0 || event.getY() < 0 || event.getX() > this.getWidth() || event.getY() > this.getHeight())
if (distance > this.getWidth() / 2F) {
this.mHoldingDown = false;
if (this.currentAnimation != null) {
this.currentAnimation.cancel();
}
this.mCurrentDrawable.setProgress(0f);
this.mCurrentDrawable.setCircleScale(0f);
}
}
return true;
}
private double distanceFromCenter(float touchX, float touchY) {
// get centers
int centerX = this.getWidth() / 2;
int centerY = this.getHeight() / 2;
// calculate distances
float distanceX = touchX - centerX;
float distanceY = touchY - centerY;
// pitagora suichi!
return Math.sqrt((distanceX * distanceX) + (distanceY * distanceY));
}
/* ownmeth */
public void setProgressListener(ProgressListener progressListener) {
this.mProgressListener = progressListener;
}
public ProgressListener getProgressListener() {
return this.mProgressListener;
}
public void activate() {
this.mCurrentDrawable = this.mActiveDrawable;
this.setBackground(this.mCurrentDrawable);
}
public void deactivate() {
this.mCurrentDrawable = this.mInactiveDrawable;
this.setBackground(this.mCurrentDrawable);
}
private Animator prepareStyle2Animation() {
AnimatorSet animation = new AnimatorSet();
final ObjectAnimator progressAnimation = ObjectAnimator.ofFloat(this.mCurrentDrawable, CircularProgressDrawable.PROGRESS_PROPERTY, 0f, 1f);
progressAnimation.setDuration(750);
progressAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
Animator innerCircleAnimation = ObjectAnimator.ofFloat(this.mCurrentDrawable, CircularProgressDrawable.CIRCLE_SCALE_PROPERTY, 0f, 1f);
innerCircleAnimation.setDuration(500);
innerCircleAnimation.addListener(new AnimatorListenerAdapter() {
final CircularProgressButton $this = CircularProgressButton.this;
@SuppressLint("MissingPermission")
@Override
public void onAnimationStart(Animator animation) {
// catch and ignore possible npe's due to ui refreshing
try {
if ($this.mProgressListener instanceof Fragment) {
((Fragment) $this.mProgressListener).getActivity().getApplicationContext();
}
if ($this.mProgressListener instanceof Activity) {
((Activity) $this.mProgressListener).getApplicationContext();
}
$this.mVibrator.vibrate(VibrationEffect.createOneShot(25, 10));
} catch (Exception ignore) {
}
}
@SuppressLint("MissingPermission")
@Override
public void onAnimationEnd(Animator animation) {
// catch and ignore possible npe's due to ui refreshing
try {
if ($this.mProgressListener instanceof Fragment) {
((Fragment) $this.mProgressListener).getActivity().getApplicationContext();
}
if ($this.mProgressListener instanceof Activity) {
((Activity) $this.mProgressListener).getApplicationContext();
}
if ($this.mHoldingDown) {
$this.mVibrator.vibrate(VibrationEffect.createOneShot(100, 10));
if ($this.mProgressListener != null) {
$this.mProgressListener.onProgressFinish();
}
}
} catch (Exception ignore) {
}
}
});
animation.playSequentially(progressAnimation, innerCircleAnimation);
return animation;
}
}

View File

@@ -0,0 +1,419 @@
package com.iesoluciones.siodrenax.utils;
/*
* Created by iedeveloper on 14/12/17.
*/
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
/**
* Circular Progress Drawable.
* <p>
* This drawable will produce a circular shape with a ring surrounding it. The ring can appear
* both filled and give a little cue when it is empty.
* <p>
* The inner circle size, the progress of the outer ring and if it is loading parameters can be
* controlled, as well the different colors for the three components.
*
* @author Saul Diaz <sefford@gmail.com>
*/
public class CircularProgressDrawable extends Drawable {
/**
* Factor to convert the factor to paint the arc.
* <p>
* In this way the developer can use a more user-friendly [0..1f] progress
*/
public static final int PROGRESS_FACTOR = 360;
/**
* Property Inner Circle Scale.
* <p>
* The inner ring is supposed to defaults stay 3/4 radius off the outer ring at (75% scale), but this
* property can make it grow or shrink via this equation: OuterRadius * Scale.
* <p>
* A 100% scale will make the inner circle to be the same radius as the outer ring.
*/
public static final String CIRCLE_SCALE_PROPERTY = "circleScale";
/**
* Property Progress of the outer circle.
* <p>
* The progress of the circle. If {@link #setIndeterminate(boolean) indeterminate flag} is set
* to FALSE, this property will be used to indicate the completion of the outer circle [0..1f].
* <p>
* If set to TRUE, the drawable will activate the loading mode, where the drawable will
* show a 90º arc which will be spinning around the outer circle as much as progress goes.
*/
public static final String PROGRESS_PROPERTY = "progress";
/**
* Property Ring color.
* <p>
* Changes the ring filling color
*/
public static final String RING_COLOR_PROPERTY = "ringColor";
/**
* Property circle color.
* <p>
* Changes the inner circle color
*/
public static final String CIRCLE_COLOR_PROPERTY = "circleColor";
/**
* Property outline color.
* <p>
* Changes the outline of the ring color.
*/
public static final String OUTLINE_COLOR_PROPERTY = "outlineColor";
/**
* Logger Tag for Logging purposes.
*/
public static final String TAG = "CircularProgressDrawable";
/**
* Paint object to draw the element.
*/
private final Paint paint;
/**
* Ring progress.
*/
protected float progress;
/**
* Color for the empty outer ring.
*/
protected int outlineColor;
/**
* Color for the completed ring.
*/
protected int ringColor;
/**
* Color for the inner circle.
*/
protected int centerColor;
protected final int fillColor;
/**
* Rectangle where the filling ring will be drawn into.
*/
protected final RectF arcElements;
/**
* Width of the filling ring.
*/
protected final int ringWidth;
/**
* Scale of the inner circle. It will affect the inner circle size on this equation:
* ([Biggest length of the Drawable] / 2) - (ringWidth / 2) * scale.
*/
protected float circleScale;
/**
* Set if it is an indeterminate
*/
protected boolean indeterminate;
/**
* Creates a new CouponDrawable.
*
* @param ringWidth Width of the filled ring
* @param circleScale Scale difference between the outer ring and the inner circle
* @param outlineColor Color for the outline color
* @param ringColor Color for the filled ring
* @param centerColor Color for the center element
*/
CircularProgressDrawable(int ringWidth, float circleScale, int outlineColor, int ringColor, int centerColor, int fillColor) {
this.progress = 0;
this.outlineColor = outlineColor;
this.ringColor = ringColor;
this.centerColor = centerColor;
this.fillColor = fillColor;
this.paint = new Paint();
this.paint.setAntiAlias(true);
this.ringWidth = ringWidth;
this.arcElements = new RectF();
this.circleScale = circleScale;
this.indeterminate = false;
}
@Override
public void draw(Canvas canvas) {
final Rect bounds = getBounds();
// Calculations on the different components sizes
int size = Math.min(bounds.height(), bounds.width());
float outerRadius = (size / 2F) - (ringWidth / 2F);
float innerRadius = outerRadius * circleScale;
float offsetX = (bounds.width() - outerRadius * 2) / 2;
float offsetY = (bounds.height() - outerRadius * 2) / 2;
// Outline Circle
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(1);
paint.setColor(outlineColor);
canvas.drawCircle(bounds.centerX(), bounds.centerY(), outerRadius, paint);
// Fill circle
paint.setStyle(Paint.Style.FILL);
paint.setColor(centerColor);
canvas.drawCircle(bounds.centerX(), bounds.centerY(), outerRadius, paint);
// Inner circle
paint.setStyle(Paint.Style.FILL);
paint.setColor(fillColor);
paint.setAlpha(255);
paint.setMaskFilter(new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL));
canvas.drawCircle(bounds.centerX(), bounds.centerY(), innerRadius, paint);
paint.setMaskFilter(null);
int halfRingWidth = ringWidth / 2;
float arcX0 = offsetX + halfRingWidth;
float arcY0 = offsetY + halfRingWidth;
float arcX = offsetX + outerRadius * 2 - halfRingWidth;
float arcY = offsetY + outerRadius * 2 - halfRingWidth;
// Outer Circle
paint.setColor(ringColor);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(ringWidth);
paint.setStrokeCap(Paint.Cap.ROUND);
arcElements.set(arcX0, arcY0, arcX, arcY);
if (indeterminate) {
canvas.drawArc(arcElements, progress, 90, false, paint);
} else {
canvas.drawArc(arcElements, 360 - 90, progress, false, paint);
}
}
@Override
public void setAlpha(int alpha) {
paint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
paint.setColorFilter(cf);
}
@Override
public int getOpacity() {
return 1 - paint.getAlpha();
}
/**
* Returns the progress of the outer ring.
* <p>
* Will output a correct value only when the indeterminate mode is set to FALSE.
*
* @return Progress of the outer ring.
*/
public float getProgress() {
return progress / PROGRESS_FACTOR;
}
/**
* Sets the progress [0..1f]
*
* @param progress Sets the progress
*/
public void setProgress(float progress) {
if (indeterminate) {
this.progress = progress;
} else {
this.progress = PROGRESS_FACTOR * progress;
}
invalidateSelf();
}
/**
* Returns the inner circle scale.
*
* @return Inner circle scale in float multiplier.
*/
public float getCircleScale() {
return circleScale;
}
/**
* Sets the inner circle scale.
*
* @param circleScale Inner circle scale.
*/
public void setCircleScale(float circleScale) {
this.circleScale = circleScale;
invalidateSelf();
}
/**
* Get the indeterminate status of the Drawable
*
* @return TRUE if the Drawable is in indeterminate mode or FALSE if it is in progress mode.
*/
public boolean isIndeterminate() {
return indeterminate;
}
/**
* Sets the indeterminate parameter.
* <p>
* The indeterminate parameter will change the behavior of the Drawable. If the indeterminate
* mode is set to FALSE, the outer ring will be able to be filled by using {@link #setProgress(float) setProgress}.
* <p>
* Otherwise the drawable will enter "loading mode" and a 90º arc will be able to be spinned around
* the inner circle.
* <p>
* <b>By default, indeterminate mode is set to FALSE.</b>
*
* @param indeterminate TRUE to activate loading mode. FALSE to activate progress mode.
*/
public void setIndeterminate(boolean indeterminate) {
this.indeterminate = indeterminate;
}
/**
* Gets the outline color.
*
* @return Outline color of the empty ring.
*/
public int getOutlineColor() {
return outlineColor;
}
/**
* Gets the filled ring color.
*
* @return Returns the filled ring color.
*/
public int getRingColor() {
return ringColor;
}
/**
* Gets the color of the inner circle.
*
* @return Inner circle color.
*/
public int getCenterColor() {
return centerColor;
}
/**
* Sets the empty progress outline color.
*
* @param outlineColor Outline color in #AARRGGBB format.
*/
public void setOutlineColor(int outlineColor) {
this.outlineColor = outlineColor;
invalidateSelf();
}
/**
* Sets the progress ring color.
*
* @param ringColor Ring color in #AARRGGBB format.
*/
public void setRingColor(int ringColor) {
this.ringColor = ringColor;
invalidateSelf();
}
/**
* Sets the inner circle color.
*
* @param centerColor Inner circle color in #AARRGGBB format.
*/
public void setCenterColor(int centerColor) {
this.centerColor = centerColor;
invalidateSelf();
}
/**
* Helper class to manage the creation of a CircularProgressDrawable
*
* @author Saul Diaz <sefford@gmail.com>
*/
public static class Builder {
/**
* Witdh of the stroke of the filled ring
*/
int ringWidth;
/**
* Color of the outline of the empty ring in #AARRGGBB mode.
*/
int outlineColor;
/**
* Color of the filled ring in #AARRGGBB mode.
*/
int ringColor;
/**
* Color of the inner circle in #AARRGGBB mode.
*/
int centerColor;
int fillcolor;
/**
* Scale between the outer ring and the inner circle
*/
float circleScale = 0f;
/**
* Sets the ring width.
*
* @param ringWidth Default ring width
* @return This builder
*/
public Builder setRingWidth(int ringWidth) {
this.ringWidth = ringWidth;
return this;
}
/**
* Sets the default empty outer ring outline color.
*
* @param outlineColor Outline color in #AARRGGBB format.
* @return This Builder
*/
public Builder setOutlineColor(int outlineColor) {
this.outlineColor = outlineColor;
return this;
}
/**
* Sets the progress ring color.
*
* @param ringColor Ring color in #AARRGGBB format.
* @return This Builder
*/
public Builder setRingColor(int ringColor) {
this.ringColor = ringColor;
return this;
}
/**
* Sets the inner circle color.
*
* @param centerColor Inner circle color in #AARRGGBB format.
* @return This builder
*/
public Builder setCenterColor(int centerColor) {
this.centerColor = centerColor;
return this;
}
public Builder setFillColor(int fillcolor) {
this.fillcolor = fillcolor;
return this;
}
/**
* Sets the inner circle scale. Defaults to 0.75.
*
* @param circleScale Inner circle scale.
* @return This builder
*/
public Builder setInnerCircleScale(float circleScale) {
this.circleScale = circleScale;
return this;
}
/**
* Creates a new CircularProgressDrawable with the requested parameters
*
* @return New CircularProgressDrawableInstance
*/
public com.iesoluciones.siodrenax.utils.CircularProgressDrawable create() {
return new com.iesoluciones.siodrenax.utils.CircularProgressDrawable(ringWidth, circleScale, outlineColor, ringColor, centerColor, fillcolor);
}
}
}

View File

@@ -0,0 +1,98 @@
package com.iesoluciones.siodrenax.utils
class Constants {
companion object {
const val LOGGED_IN = 1
const val WORKDAY_STARTED=2
const val NO_SESSION = 3
const val ORDER_IN_PROGRESS = 4
const val LOGGED_IN_MANAGER = 5
const val WORKDAY_STARTED_MANAGER = 6
const val APP_ID = "MOVIL"
const val EMPLOYEE_MANAGER: Long = 6
// CheckList
const val REVISION = "REVISION"
const val MATERIAL = "MATERIAL"
const val HERRAMIENTA = "HERRAMIENTA"
const val WORKDAY = "WORKDAY"
// Evidences
const val EVIDENCE_START = 1
const val EVIDENCE_PROCESS = 2
const val EVIDENCE_FINAL = 3
// Location
const val DEFAULT_LATITUDE = 24.7726885
const val DEFAULT_LONGITUDE = -107.4434088
// Notifications
const val ORDER_ID = "servicio_id"
// Survey
const val ID_SERVICE_TYPE_DOMESTIC: Long = 1L
// Parameters
const val DEFAULT_LITERS: String = "0"
const val HIDE_ALPHA = 0f
const val HIDE_DURATION = 200L
const val SHOW_ALPHA = 1f
const val SHOW_DURATION = 300L
// OrderDetail
const val EVIDENCE_ID: String = "evidence_id"
const val EVIDENCE_NAME: String = "evidence_name"
const val MILEAGE = "mileage"
const val IDSERVICETYPEDOMESTIC = 1L
const val EVIDENCE_CODE = 2
const val SIGNATURE_CODE = 10
const val SURVEY_CODE = 20
const val REQUEST_PERMISSIONS_REQUEST_CODE = 33
// Camera
const val COMPRESS_JPEG_QUALITY_LOW: Int = 15
const val EVIDENCIA = "Evidencia "
const val SHOW_TITLE_ALPHA = 0f
const val SHOW_TITLE_DURATION = 200L
const val SHOW_TITLE_DELAY = 1000L
// Survey
const val PDF_URL = "http://drenax.mx/assets/documents/aviso_privacidad.pdf"
const val DRIVE_READER = "https://drive.google.com/viewerng/viewer?embedded=true&url="
// Answer Type
const val ANSWER_CHECKBOX = 1
const val ANSWER_DATE = 2
const val ANSWER_EMAIL = 3
const val ANSWER_CURRENCY = 4
const val ANSWER_NUMBER = 5
const val ANSWER_TEXT = 6
// Notifications
const val EXTRA_NOTIFICATION_CLICKED: String = "notificationClicked"
// MultipleViewHolderAdapter
const val CERO = "0"
// Signature
const val ANIMATE_FADE_OUT_ALPHA = 0.0f
const val ANIMATE_FADE_OUT_DURATION = 200L
const val ANIMATE_FADE_IN_DURATION = 200L
const val ANIMATE_FADE_IN_ALPHA = 1.0f
//Manager
const val OPERATOR_ID: String = "operator_id"
const val OPERATOR_NAME = "operator_name"
//ZIP
const val BUFFER_SIZE = 6 * 1024
//SKETCH
const val BUFFER_SIZE_SKETCH = 1024 * 1024
const val BRANCH_NAME = "branch_name"
const val SKETCH_PATH = "sketch_path"
}
}

View File

@@ -0,0 +1,128 @@
package com.iesoluciones.siodrenax.utils
import android.app.AlertDialog
import android.app.Dialog
import android.app.ProgressDialog
import android.content.Context
import android.view.View
import com.iesoluciones.siodrenax.R
class CustomDialogFragment(context: Context) {
private var mContext: Context = context
private lateinit var mDialog: ProgressDialog
private lateinit var mMessage: String
private lateinit var mTitle: String
private lateinit var mSubtitle: String
private lateinit var mTitleCloseButton: String
private var mSelection = 0
private lateinit var mArrayMessage: Array<String>
private lateinit var mViewType: MessageViewType
private lateinit var mView: View
enum class MessageViewType {
RADIOBUTTON, DIALOG, ALERTDIALOG, WEBVIEWDIALOG
}
fun setTitle(title: String): CustomDialogFragment {
this.mTitle = title
return this
}
fun messageType(messageViewType: MessageViewType): CustomDialogFragment {
mViewType = messageViewType
return this
}
fun show() {
when (mViewType) {
MessageViewType.RADIOBUTTON -> showRadioButtonMessage(mTitle, mArrayMessage)
MessageViewType.DIALOG -> showDialog(mTitle, mSubtitle)
MessageViewType.ALERTDIALOG -> showAlertDialogMessage(
mTitle,
mMessage,
mTitleCloseButton
)
MessageViewType.WEBVIEWDIALOG -> showWebViewDialog(mView)
}
}
private fun showRadioButtonMessage(titulo: String, arrayMessage: Array<String>) {
val builder = AlertDialog.Builder(mContext)
builder.setTitle(titulo)
builder.setSingleChoiceItems(
arrayMessage, 1
) { _, which ->
mSelection = which
}
builder.setNegativeButton(
mContext.getString(R.string.cancel)
) { _, _ -> }
builder.setPositiveButton(
mContext.getString(R.string.accept)
) { _, _ -> }
val alert = builder.create()
alert.setCanceledOnTouchOutside(false)
alert.show()
}
fun showRadioButtonMessage(
titulo: String,
arrayMessage: Array<String>,
onClickActionButton: OnClickActionButton
): Dialog {
val builder = AlertDialog.Builder(mContext, R.style.DialogMessageTheme)
builder.setTitle(titulo)
builder.setSingleChoiceItems(arrayMessage, 0) { _, which -> mSelection = which }
builder.setPositiveButton(mContext.getString(R.string.accept)) { _, _ ->
onClickActionButton.onPositiveRadioButtonClicked(
mSelection
)
}
builder.setCancelable(false)
builder.setNegativeButton(mContext.getString(R.string.cancel)) { _, _ -> onClickActionButton.onNegativeButtonClicked() }
return builder.create()
}
private fun showDialog(titulo: String, descripcion: String) {
if (mDialog.isShowing)
mDialog.dismiss()
mDialog = ProgressDialog(mContext, R.style.DialogMessageTheme)
mDialog.setTitle(titulo)
mDialog.setMessage(descripcion)
mDialog.setCancelable(false)
mDialog.setCanceledOnTouchOutside(false)
mDialog.show()
}
private fun showWebViewDialog(view: View) {
if (mDialog.isShowing)
mDialog.dismiss()
mDialog = ProgressDialog(mContext, R.style.DialogMessageTheme)
mDialog.setView(view)
mDialog.setCancelable(false)
mDialog.setCanceledOnTouchOutside(false)
mDialog.show()
}
private fun showAlertDialogMessage(
title: String,
message: String,
positiveButton: String
): Dialog {
val builder = AlertDialog.Builder(mContext)
builder.setTitle(title)
builder.setMessage(message)
builder.setPositiveButton(positiveButton) { _, _ -> }
return builder.create()
}
interface OnClickActionButton {
fun onPositiveRadioButtonClicked(selection: Int)
fun onPositiveButtonClicked()
fun onNegativeButtonClicked()
}
}

View File

@@ -0,0 +1,96 @@
package com.iesoluciones.siodrenax.utils
import android.content.Context
import android.os.Build
import android.print.PdfPrinter
import android.print.PrintAttributes
import android.print.PrintDocumentAdapter
import android.webkit.WebChromeClient
import android.webkit.WebView
import androidx.annotation.UiThread
import java.io.File
class CustomHtmlToPdfConvertor(private val context: Context) {
private var baseUrl: String? = null
fun setBaseUrl(baseUrl: String) {
this.baseUrl = baseUrl
}
@UiThread
fun convert(
pdfLocation: File,
htmlString: String,
onPdfGenerationFailed: PdfGenerationFailedCallback? = null,
onPdfGenerated: PdfGeneratedCallback,
) {
// maintain pdf generation status
var pdfGenerationStarted = false
try {
// create new webview
val pdfWebView = WebView(context)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
pdfWebView.settings.safeBrowsingEnabled = false
}
// job name
val jobName = Math.random().toString()
// generate pdf attributes and properties
val attributes = getPrintAttributes()
// generate print document adapter
val printAdapter = getPrintAdapter(pdfWebView, jobName)
pdfWebView.webChromeClient = object : WebChromeClient() {
override fun onProgressChanged(view: WebView, newProgress: Int) {
super.onProgressChanged(view, newProgress)
// some times progress provided by this method is wrong, that's why we are getting progress directly provided by WebView
val progress = pdfWebView.progress
// when page is fully loaded, start creating PDF
if (progress == 100 && !pdfGenerationStarted) {
// change the status of pdf generation
pdfGenerationStarted = true
// generate pdf
val pdfPrinter = PdfPrinter(attributes)
pdfPrinter.generate(printAdapter, pdfLocation, onPdfGenerated)
}
}
}
// load html in WebView when it's setup is completed
pdfWebView.loadDataWithBaseURL(baseUrl, htmlString, "text/html", "utf-8", null)
} catch (e: Exception) {
onPdfGenerationFailed?.invoke(e)
}
}
private fun getPrintAdapter(pdfWebView: WebView, jobName: String): PrintDocumentAdapter {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
pdfWebView.createPrintDocumentAdapter(jobName)
} else {
pdfWebView.createPrintDocumentAdapter()
}
}
private fun getPrintAttributes(): PrintAttributes {
return PrintAttributes.Builder().apply {
setMediaSize(PrintAttributes.MediaSize.NA_LETTER)
setResolution(PrintAttributes.Resolution("pdf", Context.PRINT_SERVICE, 100, 100))
setMinMargins(PrintAttributes.Margins.NO_MARGINS)
}.build()
}
}
private typealias PdfGeneratedCallback = (File) -> Unit
private typealias PdfGenerationFailedCallback = (Exception) -> Unit

View File

@@ -0,0 +1,76 @@
package com.iesoluciones.siodrenax.utils
import android.app.Activity
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import com.google.android.material.snackbar.Snackbar
import com.iesoluciones.siodrenax.R
import java.nio.file.Files
import java.nio.file.Path
import java.text.DecimalFormat
fun Activity.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) =
Toast.makeText(this, message, duration).show()
fun Activity.toastLong(message: CharSequence, duration: Int = Toast.LENGTH_LONG) =
Toast.makeText(this, message, duration).show()
fun Activity.toast(resourceId: Int, duration: Int = Toast.LENGTH_SHORT) =
Toast.makeText(this, resourceId, duration).show()
fun Activity.snackBar(
message: CharSequence,
view: View? = findViewById(R.id.container),
duration: Int = Snackbar.LENGTH_SHORT,
action: String? = null,
actionEvt: (v: View) -> Unit = {}
) {
if (view != null) {
val snackBar = Snackbar.make(view, message, duration)
if (!action.isNullOrEmpty()) {
snackBar.setAction(action, actionEvt)
}
snackBar.show()
}
}
fun ViewGroup.inflate(layoutId: Int) = LayoutInflater.from(context).inflate(layoutId, this, false)!!
inline fun <reified T : Activity> Activity.goToActivity(noinline init: Intent.() -> Unit = {}) {
val intent = Intent(this, T::class.java)
intent.init()
startActivity(intent)
}
fun String.currencyFormat(): String {
return try {
val formatter = DecimalFormat("###,###,##0.00")
formatter.format(this.toDouble())
} catch (e: Exception) {
"0.00"
}
}
fun Path.exists(): Boolean = Files.exists(this)
fun Path.isFile(): Boolean = !Files.isDirectory(this)
fun Path.delete(): Boolean {
return if (isFile() && exists()) {
//Actual delete operation
Files.delete(this)
true
} else {
false
}
}
val Any.TAG: String
get() {
val tag = javaClass.simpleName
return if (tag.length <= 23) tag else tag.substring(0, 23)
}

View File

@@ -0,0 +1,464 @@
package com.iesoluciones.siodrenax.utils
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Handler
import android.os.Looper
import android.os.StrictMode
import android.provider.Settings
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import androidx.work.Constraints
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import com.iesoluciones.siodrenax.*
import com.iesoluciones.siodrenax.App.Companion.context
import com.iesoluciones.siodrenax.activities.LoginActivity
import com.iesoluciones.siodrenax.entities.*
import com.iesoluciones.siodrenax.fragments.dialogs.IncidentsInputFragment
import com.iesoluciones.siodrenax.fragments.dialogs.MileageInputDialogFragment
import com.iesoluciones.siodrenax.fragments.dialogs.VehicleIncidentsFragment
import com.iesoluciones.siodrenax.interfaces.OnIncidentListener
import com.iesoluciones.siodrenax.interfaces.OnIncidentShowListener
import com.iesoluciones.siodrenax.interfaces.OnMileageListener
import com.iesoluciones.siodrenax.network.HttpError
import com.iesoluciones.siodrenax.network.UnprocessableEntity
import com.iesoluciones.siodrenax.repositories.WorkdayRepository
import com.iesoluciones.siodrenax.utils.Constants.Companion.BUFFER_SIZE
import com.iesoluciones.siodrenax.utils.Constants.Companion.BUFFER_SIZE_SKETCH
import com.iesoluciones.siodrenax.workers.SyncWorker
import retrofit2.HttpException
import java.io.*
import java.net.*
import java.nio.file.Files
import java.nio.file.Paths
import java.util.*
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import javax.net.ssl.SSLPeerUnverifiedException
class HelperUtil {
private val tagIncidenceDialog = "incidenceDialog"
private val tagShowIncidenceDialog = "showIncidenceDialog"
private val tagMileageDialog = "mileageDialog"
fun parseError(context: Context, e: Throwable) {
if (e is HttpException) {
when (e.code()) {
401, 400, 403, 404, 405, 423 -> {
val error: HttpError? = HttpError.parseException(e)
if (error != null) {
AlertDialog.Builder(context, R.style.MyAlertDialogStyle)
.setMessage(error.error)
.setTitle(R.string.title_error)
.setCancelable(false)
.setPositiveButton(
App.shareInstance!!.resources.getString(R.string.button_ok),
null
)
.show()
}
}
420 -> {
val error: HttpError? = HttpError.parseException(e)
if (error != null) {
val workdayRepository = WorkdayRepository()
if (workdayRepository.validateRenewSession()) {
AlertDialog.Builder(context, R.style.MyAlertDialogStyle)
.setMessage(R.string.session_expired_message)
.setTitle(R.string.session_expired)
.setCancelable(false)
.setPositiveButton(App.shareInstance!!.resources.getString(R.string.button_renew_session)) { _, _ ->
workdayRepository.logout()
val intent = Intent(context, LoginActivity::class.java);
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
(context as Activity).startActivity(intent)
(context as Activity).finish()
}
.show()
} else {
AlertDialog.Builder(context, R.style.MyAlertDialogStyle)
.setMessage(R.string.session_expired_with_orders_message)
.setTitle(R.string.session_expired)
.setCancelable(false)
.setPositiveButton(App.shareInstance!!.resources.getString(R.string.button_ok), null)
.show()
}
}
}
422 -> {
val errors = UnprocessableEntity.parseException(e)
AlertDialog.Builder(context, R.style.MyAlertDialogStyle)
.setMessage(errors!!.errors[0])
.setTitle(errors.message)
.setCancelable(false)
.setPositiveButton(
App.shareInstance!!.resources.getString(R.string.button_ok),
null
)
.show()
}
500 -> AlertDialog.Builder(context, R.style.MyAlertDialogStyle)
.setMessage(App.shareInstance!!.resources.getString(R.string.unexpected_error))
.setTitle(App.shareInstance!!.resources.getString(R.string.title_error))
.setCancelable(false)
.setPositiveButton(context.resources.getString(R.string.button_ok), null)
.show()
}
} else if (e is IOException) {
when (e) {
is ConnectException -> AlertDialog.Builder(context, R.style.MyAlertDialogStyle)
.setMessage(App.shareInstance!!.resources.getString(R.string.unreachable_network))
.setTitle(App.shareInstance!!.resources.getString(R.string.title_error))
.setCancelable(false)
.setPositiveButton(
App.shareInstance!!.resources.getString(R.string.button_ok),
null
)
.setNeutralButton(App.shareInstance!!.resources.getString(R.string.network_settings)) { _, _ ->
context.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS))
}.show()
is SocketException -> AlertDialog.Builder(context, R.style.MyAlertDialogStyle)
.setMessage(App.shareInstance!!.resources.getString(R.string.unreachable_network))
.setTitle(App.shareInstance!!.resources.getString(R.string.title_error))
.setCancelable(false)
.setPositiveButton(
App.shareInstance!!.resources.getString(R.string.button_ok),
null
)
.setNeutralButton(App.shareInstance!!.resources.getString(R.string.network_settings)) { _, _ ->
context.startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS))
}.show()
is SocketTimeoutException -> AlertDialog.Builder(
context,
R.style.MyAlertDialogStyle
)
.setMessage(App.shareInstance!!.resources.getString(R.string.timeout))
.setTitle(App.shareInstance!!.resources.getString(R.string.title_error))
.setCancelable(false)
.setPositiveButton(
App.shareInstance!!.resources.getString(R.string.button_ok),
null
)
.show()
is UnknownHostException -> Toast.makeText(
context,
context.getString(R.string.unknown_host_exception),
Toast.LENGTH_LONG
).show()
is SSLPeerUnverifiedException -> AlertDialog.Builder(
context,
R.style.MyAlertDialogStyle
)
.setMessage(App.shareInstance!!.resources.getString(R.string.certificate_error_message))
.setTitle(App.shareInstance!!.resources.getString(R.string.certificate_error))
.setCancelable(false)
.setPositiveButton(
App.shareInstance!!.resources.getString(R.string.button_ok),
null
)
.show()
else -> Toast.makeText(
context,
context.getString(R.string.unexpected_error),
Toast.LENGTH_LONG
).show()
}
}
}
fun incidentShowDialogFragment(
fm: FragmentManager,
listener: OnIncidentShowListener,
incidence: String?
) {
val ft = fm.beginTransaction()
val prev = fm.findFragmentByTag(tagShowIncidenceDialog)
if (prev != null) {
ft.remove(prev)
}
ft.addToBackStack(null)
Handler(Looper.getMainLooper()).postDelayed({
val dialogFragment: DialogFragment = VehicleIncidentsFragment.newInstance(
listener,
incidence
)
dialogFragment.show(ft, tagShowIncidenceDialog)
}, 500)
}
fun createEvidences(order: Order) {
val evidence = evidenceBox.query()
.equal(Evidence_.idOrder, order.id)
.and().equal(Evidence_.idRequest, order.idRequest)
.build()
.findFirst()
if(evidence == null)
this.createEvidenceHolders(order.id, order.idRequest)
}
private fun createEvidenceHolders(idOrder: Long, idRequest: Long) {
for (i in 1..9) {
val evidence = Evidence()
evidence.idOrder = idOrder
evidence.idRequest = idRequest
when (i) {
1, 2, 3 -> {
evidence.type = Constants.EVIDENCE_START
evidence.evidenceNo = ((i.toString() + "").toLong())
}
4, 5, 6 -> {
evidence.type = Constants.EVIDENCE_PROCESS
evidence.evidenceNo = ((i - 3).toString() + "").toLong()
}
7, 8, 9 -> {
evidence.type = Constants.EVIDENCE_FINAL
evidence.evidenceNo = ((i - 6).toString() + "").toLong()
}
else -> {
evidence.type = Constants.EVIDENCE_START + 1
evidence.evidenceNo = (i.toString() + "").toLong()
}
}
try {
evidenceBox.put(evidence)
} catch (ex: Exception) {
Log.e("CREAR EVIDENCIA", ex.toString())
}
}
}
fun mileageDialogFragment(fm: FragmentManager, listener: OnMileageListener) {
val ft = fm.beginTransaction()
val prev = fm.findFragmentByTag(tagMileageDialog)
if (prev != null) {
ft.remove(prev)
}
ft.addToBackStack(null)
Handler(Looper.getMainLooper()).postDelayed({
val dialogFragment: DialogFragment =
MileageInputDialogFragment.newInstance(listener)
dialogFragment.show(ft, tagMileageDialog)
}, 0)
}
fun incidentDialogFragment(fm: FragmentManager, listener: OnIncidentListener) {
val ft = fm.beginTransaction()
val prev = fm.findFragmentByTag(tagIncidenceDialog)
if (prev != null) {
ft.remove(prev)
}
ft.addToBackStack(null)
Handler(Looper.getMainLooper()).postDelayed({
val dialogFragment: DialogFragment = IncidentsInputFragment.newInstance(listener)
dialogFragment.show(ft, tagIncidenceDialog)
}, 500)
}
fun formatTime(millis: Long): String {
val secs = millis / 1000
return String.format("%02d:%02d:%02d", secs / 3600, secs % 3600 / 60, secs % 60)
}
fun decodeFromFile(path: String, sampleSize: Int): Bitmap {
val options2 = BitmapFactory.Options()
options2.inSampleSize = sampleSize
return if (sampleSize != 0) BitmapFactory.decodeFile(
path,
options2
) else BitmapFactory.decodeFile(
path,
null
)
}
fun saveBitmap(filePath: File, filename: String, bitmap: Bitmap): String? {
val file = File(filePath, filename)
val fos: FileOutputStream
try {
fos = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos)
fos.flush()
} catch (e: java.lang.Exception) {
e.printStackTrace()
return null
}
return file.absolutePath
}
fun startSync() {
//System.gc()
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val syncWorker = OneTimeWorkRequest.Builder(SyncWorker::class.java)
.setConstraints(constraints)
.build()
Toast.makeText(
context,
context!!.getString(R.string.toast_sync_progress),
Toast.LENGTH_LONG
).show()
WorkManager.getInstance().enqueue(syncWorker)
}
/**
* Extracts first letter of Name and LastName sent in Params
*
* @param name Name
* @param lastname Last Name
* @return String with 2 Letters in UpperCase
*/
fun getInitials(name: String?, lastname: String?): String {
var initials = ""
if (name != null) if (name.isNotEmpty() && name.substring(0, 1) != " ") {
initials += name.substring(0, 1)
}
if (lastname != null) if (lastname.isNotEmpty() && lastname.substring(0, 1) != " ") {
initials += lastname.substring(0, 1)
}
return initials.toUpperCase(Locale.ROOT)
}
@Throws(IOException::class)
fun zip(files: Array<String>, zipFile: String): String {
var origin: BufferedInputStream?
val out = ZipOutputStream(BufferedOutputStream(FileOutputStream(zipFile)))
try {
val data = ByteArray(BUFFER_SIZE)
for (i in files.indices) {
val fi = FileInputStream(files[i])
origin = BufferedInputStream(fi, BUFFER_SIZE)
try {
val entry = ZipEntry(files[i].substring(files[i].lastIndexOf("/") + 1))
out.putNextEntry(entry)
var count: Int
while (origin.read(data, 0, BUFFER_SIZE)
.also { count = it } != -1
) {
out.write(data, 0, count)
}
} finally {
origin.close()
}
}
} finally {
out.close()
}
return zipFile
}
fun deleteFile(path: String): Boolean{
val p = Paths.get(path)
return p.delete()
}
fun encodeBase64(filePath: String): String?{
return try {
val bytes = File(filePath).readBytes()
Base64.getEncoder().encodeToString(bytes)
}catch (e: Exception){
null
}
}
fun encodeBase64WithData(filePath: String): String?{
return try {
val bytes = File(filePath).readBytes()
val base64 = Base64.getEncoder().encodeToString(bytes)
val extension = filePath.split(".").last()
"data:image/$extension;base64,$base64"
}catch (e: Exception){
null
}
}
fun scaleBitmap(rotated: Bitmap, maxSize: Int = 300): Bitmap? {
val outWidth: Int
val outHeight: Int
val inWidth = rotated.width
val inHeight = rotated.height
if (inWidth > inHeight) {
outWidth = maxSize
outHeight = inHeight * maxSize / inWidth
} else {
outHeight = maxSize
outWidth = inWidth * maxSize / inHeight
}
return Bitmap.createScaledBitmap(rotated, outWidth, outHeight, false)
}
fun downloadFile(fileUrl: String?, directory: File?, order: Order?) {
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
StrictMode.setThreadPolicy(policy)
try {
val url = URL(fileUrl)
val urlConnection: HttpURLConnection = url.openConnection() as HttpURLConnection
urlConnection.connect()
val inputStream: InputStream = urlConnection.inputStream
val fileOutputStream = FileOutputStream(directory)
val buffer = ByteArray(BUFFER_SIZE_SKETCH)
var bufferLength = 0
while (inputStream.read(buffer).also { bufferLength = it } > 0) {
fileOutputStream.write(buffer, 0, bufferLength)
}
fileOutputStream.close()
if(order != null){
order.sketchPath = directory!!.absolutePath
orderBox.put(order)
}
} catch (e: FileNotFoundException) {
e.printStackTrace()
} catch (e: MalformedURLException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
}
fun downloadSketchs(filesDir:String, orders: List<Order>){
if(!File("$filesDir/pdf_sketchs").isDirectory){
Files.createDirectory(Paths.get("$filesDir/pdf_sketchs"))
}
for (order in orders) {
if(order.sketchName != null && order.sketchPath == null){
downloadFile("${BuildConfig.STORAGE_URL}pdf_croquis/${order.sketchName}", File("$filesDir/pdf_sketchs/${order.sketchName}"), order)
}
}
}
}

View File

@@ -0,0 +1,129 @@
package com.iesoluciones.siodrenax.utils;
import static com.iesoluciones.siodrenax.utils.Constants.COMPRESS_JPEG_QUALITY_LOW;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.media.ExifInterface;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class ImageUtils {
public static ImageUtils mInstant;
public static ImageUtils getInstant(){
if(mInstant==null){
mInstant = new ImageUtils();
}
return mInstant;
}
public Bitmap getCompressedBitmap(String imagePath) {
float maxHeight = 1920.0f;
float maxWidth = 1080.0f;
Bitmap scaledBitmap = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bmp = BitmapFactory.decodeFile(imagePath, options);
int actualHeight = options.outHeight;
int actualWidth = options.outWidth;
float imgRatio = (float) actualWidth / (float) actualHeight;
float maxRatio = maxWidth / maxHeight;
if (actualHeight > maxHeight || actualWidth > maxWidth) {
if (imgRatio < maxRatio) {
imgRatio = maxHeight / actualHeight;
actualWidth = (int) (imgRatio * actualWidth);
actualHeight = (int) maxHeight;
} else if (imgRatio > maxRatio) {
imgRatio = maxWidth / actualWidth;
actualHeight = (int) (imgRatio * actualHeight);
actualWidth = (int) maxWidth;
} else {
actualHeight = (int) maxHeight;
actualWidth = (int) maxWidth;
}
}
options.inSampleSize = calculateInSampleSize(options, actualWidth, actualHeight);
options.inJustDecodeBounds = false;
options.inDither = false;
options.inPurgeable = true;
options.inInputShareable = true;
options.inTempStorage = new byte[16 * 1024];
try {
bmp = BitmapFactory.decodeFile(imagePath, options);
} catch (OutOfMemoryError exception) {
exception.printStackTrace();
}
try {
scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.ARGB_8888);
} catch (OutOfMemoryError exception) {
exception.printStackTrace();
}
float ratioX = actualWidth / (float) options.outWidth;
float ratioY = actualHeight / (float) options.outHeight;
float middleX = actualWidth / 2.0f;
float middleY = actualHeight / 2.0f;
Matrix scaleMatrix = new Matrix();
scaleMatrix.setScale(ratioX, ratioY, middleX, middleY);
Canvas canvas = new Canvas(scaledBitmap);
canvas.setMatrix(scaleMatrix);
canvas.drawBitmap(bmp, middleX - bmp.getWidth() / 2, middleY - bmp.getHeight() / 2, new Paint(Paint.FILTER_BITMAP_FLAG));
ExifInterface exif = null;
try {
exif = new ExifInterface(imagePath);
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0);
Matrix matrix = new Matrix();
if (orientation == 6) {
matrix.postRotate(90);
} else if (orientation == 3) {
matrix.postRotate(180);
} else if (orientation == 8) {
matrix.postRotate(270);
}
scaledBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
} catch (IOException e) {
e.printStackTrace();
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, COMPRESS_JPEG_QUALITY_LOW, out);
byte[] byteArray = out.toByteArray();
Bitmap updatedBitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
return updatedBitmap;
}
private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
final float totalPixels = width * height;
final float totalReqPixelsCap = reqWidth * reqHeight * 2;
while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
inSampleSize++;
}
return inSampleSize;
}
}

View File

@@ -0,0 +1,34 @@
package com.iesoluciones.siodrenax.utils
import android.content.Context
import android.graphics.Rect
import android.util.TypedValue
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ItemDecoration
class ListPaddingDecoration(context: Context) : ItemDecoration() {
private val mPadding: Int
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
val itemPosition = parent.getChildAdapterPosition(view)
if (itemPosition == RecyclerView.NO_POSITION) {
return
}
if (itemPosition == 0) {
outRect.top = mPadding
}
val adapter = parent.adapter
if (adapter != null && itemPosition == adapter.itemCount - 1) {
outRect.bottom = mPadding
}
}
companion object {
private const val PADDING_IN_DIPS = 8
}
init {
val metrics = context.resources.displayMetrics
mPadding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, PADDING_IN_DIPS.toFloat(), metrics).toInt()
}
}

Some files were not shown because too many files have changed in this diff Show More