From f96324d5bc7f3fcc4ff72c43e8c2e78f04ebae19 Mon Sep 17 00:00:00 2001 From: peterluo3131 Date: Tue, 6 Jun 2023 10:22:24 -0400 Subject: [PATCH] feature(#12): fix error that app is requesting with incorrect uuid --- app/build.gradle | 2 +- .../chatgptwrapper/MyApplication.kt | 5 + .../chatgptwrapper/activites/HomeActivity.kt | 52 ++--- .../dialogs/CommonConfirmDialog.kt | 6 +- .../chatgptwrapper/fragments/ChatFragment.kt | 216 +++++++++--------- .../models/requestmodels/RequestBodyModel.kt | 5 +- .../requestmodels/RequestTrainContactModel.kt | 5 +- .../chatgptwrapper/services/api/HttpClient.kt | 2 +- .../chatgptwrapper/utils/Constants.java | 5 - .../matthaigh27/chatgptwrapper/utils/Utils.kt | 80 +++++-- app/src/main/res/drawable/img_test.jpg | Bin 34245 -> 0 bytes .../main/res/layout/view_search_contact.xml | 1 + app/src/main/res/values/dimen.xml | 3 +- 13 files changed, 204 insertions(+), 178 deletions(-) delete mode 100644 app/src/main/res/drawable/img_test.jpg diff --git a/app/build.gradle b/app/build.gradle index bf2cef5..e5098b1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,7 +13,7 @@ android { applicationId "com.matthaigh27.chatgptwrapper" minSdk 28 targetSdk 33 - versionCode 2 + versionCode 7 versionName "1.5" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/com/matthaigh27/chatgptwrapper/MyApplication.kt b/app/src/main/java/com/matthaigh27/chatgptwrapper/MyApplication.kt index cea54b5..f34f843 100644 --- a/app/src/main/java/com/matthaigh27/chatgptwrapper/MyApplication.kt +++ b/app/src/main/java/com/matthaigh27/chatgptwrapper/MyApplication.kt @@ -1,6 +1,7 @@ package com.matthaigh27.chatgptwrapper import android.Manifest +import android.annotation.SuppressLint import android.app.Application import android.app.NotificationChannel import android.app.NotificationManager @@ -19,6 +20,7 @@ class MyApplication : Application() { private var mFCMToken: String = String() private var mUUID: String = String() + @SuppressLint("HardwareIds") override fun onCreate() { super.onCreate() @@ -26,6 +28,9 @@ class MyApplication : Application() { // on below line we are getting device id. mUUID = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID) appContext = applicationContext as MyApplication + + Log.v("risingandroid mUUID: ", mUUID) + Log.v("risingandroid FCMToken: ", mFCMToken) } private fun initToken() { diff --git a/app/src/main/java/com/matthaigh27/chatgptwrapper/activites/HomeActivity.kt b/app/src/main/java/com/matthaigh27/chatgptwrapper/activites/HomeActivity.kt index be596ac..0a11074 100644 --- a/app/src/main/java/com/matthaigh27/chatgptwrapper/activites/HomeActivity.kt +++ b/app/src/main/java/com/matthaigh27/chatgptwrapper/activites/HomeActivity.kt @@ -34,36 +34,33 @@ class HomeActivity : AppCompatActivity() { } private fun requestPermission() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - val notGrantedPermissions = PERMISSIONS.filter { - checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED - } + val notGrantedPermissions = PERMISSIONS.filter { + checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED + } - if (notGrantedPermissions.isNotEmpty()) { - if (shouldShowRequestPermissionRationale(notGrantedPermissions[0])) { - // show custom permission rationale - val confirmDialog = CommonConfirmDialog(this) - confirmDialog.setMessage("This app requires SMS, Contacts and Phone permissions to function properly. Please grant the necessary permissions.") - confirmDialog.setOnClickListener(object : - CommonConfirmDialog.OnConfirmButtonClickListener { - override fun onPositiveButtonClick() { - requestPermissions(notGrantedPermissions.toTypedArray(), PERMISSIONS_REQUEST_CODE) - } + if (notGrantedPermissions.isNotEmpty()) { + if (shouldShowRequestPermissionRationale(notGrantedPermissions[0])) { + // show custom permission rationale + val confirmDialog = CommonConfirmDialog(this) + confirmDialog.setMessage("This app requires SMS, Contacts and Phone permissions to function properly. Please grant the necessary permissions.") + confirmDialog.setOnClickListener(object : + CommonConfirmDialog.OnConfirmButtonClickListener { + override fun onPositiveButtonClick() { + requestPermissions( + notGrantedPermissions.toTypedArray(), PERMISSIONS_REQUEST_CODE + ) + } - override fun OnNegativeButtonClick() { - finish() - } - }) - confirmDialog.show() - } else { - requestPermissions(notGrantedPermissions.toTypedArray(), PERMISSIONS_REQUEST_CODE) - } + override fun onNegativeButtonClick() { + finish() + } + }) + confirmDialog.show() } else { - // Permissions already granted, navigate to your desired fragment - navigateToChatFragment() + requestPermissions(notGrantedPermissions.toTypedArray(), PERMISSIONS_REQUEST_CODE) } } else { - // Permissions already granted for pre-M devices, navigate to your desired fragment + // Permissions already granted, navigate to your desired fragment navigateToChatFragment() } } @@ -75,9 +72,7 @@ class HomeActivity : AppCompatActivity() { @SuppressLint("MissingSuperCall") override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray + requestCode: Int, permissions: Array, grantResults: IntArray ) { when (requestCode) { PERMISSIONS_REQUEST_CODE -> { @@ -85,7 +80,6 @@ class HomeActivity : AppCompatActivity() { // Permissions granted, navigate to your desired fragment navigateToChatFragment() } else { - Log.v("rising", "hello") requestPermission() } return diff --git a/app/src/main/java/com/matthaigh27/chatgptwrapper/dialogs/CommonConfirmDialog.kt b/app/src/main/java/com/matthaigh27/chatgptwrapper/dialogs/CommonConfirmDialog.kt index a658977..5cea790 100644 --- a/app/src/main/java/com/matthaigh27/chatgptwrapper/dialogs/CommonConfirmDialog.kt +++ b/app/src/main/java/com/matthaigh27/chatgptwrapper/dialogs/CommonConfirmDialog.kt @@ -52,13 +52,15 @@ class CommonConfirmDialog(context: Context) : Dialog(context), View.OnClickListe mTvMessage?.text = mMessage } + override fun onClick(view: View?) { when (view?.id) { R.id.btn_ok -> { mClickListener.onPositiveButtonClick() } + R.id.btn_cancel -> { - mClickListener.OnNegativeButtonClick() + mClickListener.onNegativeButtonClick() } } @@ -67,6 +69,6 @@ class CommonConfirmDialog(context: Context) : Dialog(context), View.OnClickListe interface OnConfirmButtonClickListener { fun onPositiveButtonClick() - fun OnNegativeButtonClick() + fun onNegativeButtonClick() } } \ No newline at end of file diff --git a/app/src/main/java/com/matthaigh27/chatgptwrapper/fragments/ChatFragment.kt b/app/src/main/java/com/matthaigh27/chatgptwrapper/fragments/ChatFragment.kt index dc04933..ed23b5b 100644 --- a/app/src/main/java/com/matthaigh27/chatgptwrapper/fragments/ChatFragment.kt +++ b/app/src/main/java/com/matthaigh27/chatgptwrapper/fragments/ChatFragment.kt @@ -8,15 +8,12 @@ import android.view.ViewGroup import android.annotation.SuppressLint import android.app.ActionBar.LayoutParams import android.content.* -import android.database.Cursor import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri import android.os.Build import android.os.StrictMode import android.os.StrictMode.ThreadPolicy -import android.provider.ContactsContract -import android.provider.ContactsContract.Contacts import android.provider.MediaStore import android.telephony.SmsManager import android.util.Log @@ -34,12 +31,10 @@ import com.matthaigh27.chatgptwrapper.MyApplication import com.matthaigh27.chatgptwrapper.R import com.matthaigh27.chatgptwrapper.adapters.ChatAdapter import com.matthaigh27.chatgptwrapper.database.MyDatabase -import com.matthaigh27.chatgptwrapper.database.entity.ContactEntity import com.matthaigh27.chatgptwrapper.database.entity.ImageEntity import com.matthaigh27.chatgptwrapper.dialogs.ImagePickerDialog import com.matthaigh27.chatgptwrapper.dialogs.ImagePickerDialog.OnPositiveButtonClickListener import com.matthaigh27.chatgptwrapper.models.* -import com.matthaigh27.chatgptwrapper.models.common.ContactModel import com.matthaigh27.chatgptwrapper.models.common.HelpCommandModel import com.matthaigh27.chatgptwrapper.models.common.HelpPromptModel import com.matthaigh27.chatgptwrapper.models.viewmodels.ChatMessageModel @@ -58,7 +53,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import okhttp3.* -import org.json.JSONArray import org.json.JSONException import org.json.JSONObject import java.io.* @@ -67,6 +61,7 @@ import kotlin.collections.ArrayList class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { private lateinit var rootView: View + private var mContext: Context? = null /** ui components for chatlist recyclerview */ private lateinit var mRvChatList: RecyclerView @@ -86,6 +81,8 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { private var mMessageList: ArrayList = ArrayList() private lateinit var mAdapter: ChatAdapter + /** when a user selects image by camera or gallery, + * these two variables are used to save image source and name */ private var mSelectedImage: ByteArray? = null private var mSelectedImageName: String = "" @@ -103,22 +100,26 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { /** list of help prompt commands */ private var mHelpPromptList: ArrayList? = null + /** animation variable for loading spinner */ private val rotate = RotateAnimation( 0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f ) - var mRoomDataHandler: MyDatabase? = null - - private var mContext: Context? = null + /** room database handler for local database */ + private lateinit var mRoomDataHandler: MyDatabase + /** status variable that checks if widget in chatting interface does exist */ private var mIsExistWidget: Boolean = false + /** + * this is invoked when users click the message icon to send sms on contact detail dialog + * that is shown when a user search contacts + */ private var mSMSOnClickListener: ContactDetailItem.OnSMSClickListener? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { - rootView = inflater.inflate(R.layout.fragment_chat, container, false) init() return rootView @@ -130,53 +131,32 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { initView() initDatabase() - fetchImages() - + trainImages() getAllPromptCommands() - trainContacts() } - private fun trainContacts() { - showLoading(true, "Train Contacts") - val contacts = Utils.instance.getContacts(mContext!!) - CoroutineScope(Dispatchers.Main).launch { - val changedContacts = Utils.instance.getChangedContacts(contacts, mRoomDataHandler!!) - httpClient.trainContacts(changedContacts) - } - - } private fun getAllPromptCommands() { showLoading(true, "Loading Help Prompt Data") httpClient.getALlHelpPromptCommands() } + private fun initEnvironment() { val policy = ThreadPolicy.Builder().permitAll().build() StrictMode.setThreadPolicy(policy) } private fun initValues() { + mContext = context + httpClient = HttpClient(this) rotate.duration = 3000 rotate.repeatCount = Animation.INFINITE rotate.interpolator = LinearInterpolator() - mContext = context - mSMSOnClickListener = object: ContactDetailItem.OnSMSClickListener { - override fun onSMSClickListener(phoneNumber: String) { - addMessage( - "SMS", false, false, true, MSG_WIDGET_TYPE_SMS, phoneNumber - ) - } - - override fun onVoiceCallListener(phoneNumber: String, toName: String) { - addMessage( - "You made a voice call to $toName($phoneNumber)", false, false - ) - } - } + initSMSOnClickListener() } private fun initView() { @@ -244,6 +224,29 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { mRoomDataHandler = MyDatabase.getDatabase(mContext!!) } + private fun initSMSOnClickListener() { + mSMSOnClickListener = object : ContactDetailItem.OnSMSClickListener { + override fun onSMSClickListener(phoneNumber: String) { + addMessage( + "SMS", + isMe = false, + isSend = false, + isWidget = true, + widgetType = MSG_WIDGET_TYPE_SMS, + widgetDescription = phoneNumber + ) + } + + override fun onVoiceCallListener(phoneNumber: String, toName: String) { + addMessage( + message = "You made a voice call to $toName($phoneNumber)", + isMe = false, + isSend = false + ) + } + } + } + /** * set loading spinner visible */ @@ -266,9 +269,7 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { if (visible) { mSpLoading.startAnimation(rotate) mLlLoading.visibility = View.VISIBLE - mTvLoading.text = text.ifEmpty { - "" - } + mTvLoading.text = text } else { mSpLoading.clearAnimation() mLlLoading.visibility = View.GONE @@ -284,7 +285,7 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { mIvLoadedPhoto.setImageBitmap( BitmapFactory.decodeByteArray( - imageByteArray, 0, imageByteArray.size + /* data = */ imageByteArray, /* offset = */ 0, /* length = */ imageByteArray.size ) ) } @@ -309,11 +310,11 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { */ private fun getImageResponse(imageName: String, imageDesc: String) { if (imageName.isEmpty() && imageDesc.isNotEmpty()) { - addMessage(imageDesc, false) + addMessage(message = imageDesc, isMe = false) return } - showLoading(true, LOADING_DOWNLOADING_IMAGE) + showLoading(visible = true, text = LOADING_DOWNLOADING_IMAGE) val imageNameToUpload = "images/$imageName" @@ -322,20 +323,34 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { try { val image = Utils.instance.getBitmapFromURL(uri.toString()) if (image == null) showToast("can not get bitmap from url") - val baos = ByteArrayOutputStream() - image!!.compress(Bitmap.CompressFormat.JPEG, 100, baos) - mSelectedImage = baos.toByteArray() + val byteArrayOutputStream = ByteArrayOutputStream() + + val isSuccess = + image!!.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream) + if (!isSuccess) { + showToast("Fail to compress image") + } + mSelectedImage = byteArrayOutputStream.toByteArray() } catch (e: Exception) { - showToast("cannnot get downloadurl from firebase store") + showToast("cannot get download url from firebase store") e.printStackTrace() } - addMessage(imageDesc, false) - showLoading(false) + addMessage(message = imageDesc, isMe = false) + showLoading(visible = false) } return } + override fun onResume() { + super.onResume() + updateChangedUserData() + } + + private fun updateChangedUserData() { + trainContacts() + } + /** * Open Help Command Widget with analysing string command */ @@ -346,7 +361,11 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { when (command.mainCommandName) { "sms" -> { addMessage( - "SMS", false, false, true, MSG_WIDGET_TYPE_SMS + message = "SMS", + isMe = false, + isSend = false, + isWidget = true, + widgetType = MSG_WIDGET_TYPE_SMS ) } @@ -355,17 +374,17 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { if (model.name == command.mainCommandName) { addMessage( "Help Prompt Command", - true, - false, - true, - MSG_WIDGET_TYPE_HELP_PRMOPT, - model.toString() + isMe = true, + isSend = false, + isWidget = true, + widgetType = MSG_WIDGET_TYPE_HELP_PRMOPT, + widgetDescription = model.toString() ) return } } addMessage( - "No such command name exists.", false, false + message = "No such command name exists.", isMe = false, isSend = false ) } } @@ -377,7 +396,8 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { mHelpPromptList!!.forEach { model -> strHelpList += "\n- " + model.name } - addMessage(usage + strHelpList, false, false) + + addMessage(message = usage + strHelpList, isMe = false, isSend = false) } else { var strHelpDesc = "" mHelpPromptList!!.forEach { model -> @@ -389,10 +409,10 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { strHelpDesc = "description: " + model.description + "\ntags:" + strTags } } - if (strHelpDesc == "") addMessage( - "No such command name exists.", false, false + if (strHelpDesc.isEmpty()) addMessage( + message = "No such command name exists.", isMe = false, isSend = false ) - else addMessage(strHelpDesc, false, false) + else addMessage(message = strHelpDesc, isMe = false, isSend = false) } } } catch (e: Exception) { @@ -411,6 +431,7 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { * @param isSend is boolean that checks if you send request to server * @param isWidget is boolean that checks if message item has widget * @param widgetType is type of Widget ex: SMS, HELP_COMMAND, etc + * @param widgetDescription is string that saves information for widget */ @SuppressLint("NotifyDataSetChanged") private fun addMessage( @@ -421,9 +442,9 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { widgetType: String = "", widgetDescription: String = "" ) { - if ((message == "" && mSelectedImage == null) || mIsExistWidget) return - if (isWidget == true) { - if(widgetType != MSG_WIDGET_TYPE_SEARCH_CONTACT) { + if ((message.isEmpty() && mSelectedImage == null) || mIsExistWidget) return + if (isWidget) { + if (widgetType != MSG_WIDGET_TYPE_SEARCH_CONTACT) { mIsExistWidget = true } } @@ -447,7 +468,7 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { * if users picked some image from camera or gallery, the image upload to firebase store * mSelectedImageName is uuid created uploading to firebase store */ - if (mSelectedImageName != "") { + if (mSelectedImageName.isNotEmpty()) { msg.imageName = mSelectedImageName mSelectedImageName = "" } @@ -458,7 +479,7 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { mMessageList.add(msg) mAdapter.notifyDataSetChanged() mEtMessage.setText("") - mRvChatList.scrollTo(1000, -1000) + mRvChatList.scrollTo(/* x = */ 1000, /* y = */ -1000) mRvChatList.scrollToPosition(mMessageList.size - 1) } @@ -472,7 +493,7 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { return } - showLoading(true, LOADING_ASKING_TO_GPT) + showLoading(visible = true, text = LOADING_ASKING_TO_GPT) if (msg.image != null) { httpClient.callImageRelatedness(msg.imageName) } else { @@ -518,7 +539,7 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { * calls when finish picking image * * @param imageByteData is bytearray of picked image - * @param type if users are goingto upload image or pick image for query + * @param type if users are going to upload image or pick image for query */ private fun pickedImage(imageByteData: ByteArray, type: String) { if (type == "image_upload") { @@ -555,11 +576,10 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { CoCo.with(activity!!).take(Utils.instance.createSDCardFile()) .start(object : CoCoAdapter() { override fun onSuccess(data: TakeResult) { - val byteArray: ByteArray? = + val byteArray: ByteArray = Utils.instance.getBytesFromPath(data.savedFile!!.absolutePath) - if (byteArray == null) showToast("cannot get bytes from path") pickedImage( - byteArray!!, mImagePickerType + byteArray, mImagePickerType ) } @@ -753,59 +773,38 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { return listOfImageUris } - /** - * @param path local path for converting to ByteArray - * @return ByteArray data converted from image local path - */ - private fun getBytesFromPath(path: String?): ByteArray? { - try { - val stream = FileInputStream(path) - val byteArray = stream.readBytes() - stream.close() - return byteArray - } catch (e: IOException) { - showToast("cannot get bytes from path") - e.printStackTrace() - } - return null - } - - private fun fetchImages() { + private fun trainImages() { CoroutineScope(Dispatchers.IO).launch { - val images = queryImagesFromExternalStorage(mContext!!.contentResolver) - val originalImages = mRoomDataHandler!!.imageDao().getAllImages() + val images = queryImagesFromExternalStorage(requireContext().contentResolver) + val originalImages = mRoomDataHandler.imageDao().getAllImages() images.forEach { uri -> - var existFlag = false - val path = getRealPathFromUri(mContext!!, uri) + var isExist = false + val path = Utils.instance.getRealPathFromUri(requireContext(), uri) for (i in originalImages.indices) { val entity: ImageEntity = originalImages[i] if (entity.path == path) { - existFlag = true + isExist = true break } } - if (!existFlag) { - val byteArray = getBytesFromPath(path) - val uuid = uploadImageToFirebaseStorage(byteArray!!) + if (!isExist) { + val byteArray = Utils.instance.getBytesFromPath(path) + val uuid = uploadImageToFirebaseStorage(byteArray) - Log.d(TAG, uuid.toString()) - mRoomDataHandler!!.imageDao().insertImage(ImageEntity(0, path!!, "${uuid}")) + if (path != null) + mRoomDataHandler.imageDao().insertImage(ImageEntity(0, path, "$uuid")) } } } } - fun getRealPathFromUri(context: Context, contentUri: Uri?): String? { - var cursor: Cursor? = null - return try { - val proj = arrayOf(MediaStore.Images.Media.DATA) - cursor = context.contentResolver.query(contentUri!!, proj, null, null, null) - val column_index: Int = cursor!!.getColumnIndexOrThrow(MediaStore.Images.Media.DATA) - cursor.moveToFirst() - cursor.getString(column_index) - } finally { - cursor?.close() + private fun trainContacts() { + showLoading(true, "Train Contacts") + val contacts = Utils.instance.getContacts(mContext!!) + CoroutineScope(Dispatchers.Main).launch { + val changedContacts = Utils.instance.getChangedContacts(contacts, mRoomDataHandler) + httpClient.trainContacts(changedContacts) } } @@ -814,7 +813,6 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { val smsManager = SmsManager.getDefault() val parts = smsManager.divideMessage(message) smsManager.sendMultipartTextMessage(phoneNumber, null, parts, null, null) - showToast("Sent SMS") } catch (e: SecurityException) { e.printStackTrace() } catch (e: Exception) { @@ -824,7 +822,7 @@ class ChatFragment : Fragment(), OnClickListener, HttpRisingInterface { @SuppressLint("UseRequireInsteadOfGet") fun runOnUIThread(action: () -> Unit) { - activity!!.runOnUiThread { + requireActivity().runOnUiThread { action() } } diff --git a/app/src/main/java/com/matthaigh27/chatgptwrapper/models/requestmodels/RequestBodyModel.kt b/app/src/main/java/com/matthaigh27/chatgptwrapper/models/requestmodels/RequestBodyModel.kt index e2e2a90..7c5451d 100644 --- a/app/src/main/java/com/matthaigh27/chatgptwrapper/models/requestmodels/RequestBodyModel.kt +++ b/app/src/main/java/com/matthaigh27/chatgptwrapper/models/requestmodels/RequestBodyModel.kt @@ -4,7 +4,7 @@ import com.matthaigh27.chatgptwrapper.MyApplication import org.json.JSONException import org.json.JSONObject -class RequestBodyModel { +class RequestBodyModel(builder: Builder) { /** this identify request type * example: it will be 'message' when users send message, 'image' when users upload image @@ -15,12 +15,11 @@ class RequestBodyModel { var imageName: String = "" var uuid: String = "" - constructor(builder: Builder) { + init { this.token = MyApplication.appContext.getFCMToken() this.uuid = MyApplication.appContext.getUUID() this.message = builder.message this.imageName = builder.imageName - this.uuid = builder.uuid } @Throws(JSONException::class) diff --git a/app/src/main/java/com/matthaigh27/chatgptwrapper/models/requestmodels/RequestTrainContactModel.kt b/app/src/main/java/com/matthaigh27/chatgptwrapper/models/requestmodels/RequestTrainContactModel.kt index 45dbab9..9056f6b 100644 --- a/app/src/main/java/com/matthaigh27/chatgptwrapper/models/requestmodels/RequestTrainContactModel.kt +++ b/app/src/main/java/com/matthaigh27/chatgptwrapper/models/requestmodels/RequestTrainContactModel.kt @@ -7,7 +7,7 @@ import org.json.JSONArray import org.json.JSONException import org.json.JSONObject -class RequestTrainContactModel { +class RequestTrainContactModel(builder: Builder) { /** this identify request type * example: it will be 'message' when users send message, 'image' when users upload image @@ -17,11 +17,10 @@ class RequestTrainContactModel { var contacts = JSONArray() var uuid: String = "" - constructor(builder: Builder) { + init { this.token = MyApplication.appContext.getFCMToken() this.uuid = MyApplication.appContext.getUUID() this.contacts = builder.contacts - this.uuid = builder.uuid } @Throws(JSONException::class) diff --git a/app/src/main/java/com/matthaigh27/chatgptwrapper/services/api/HttpClient.kt b/app/src/main/java/com/matthaigh27/chatgptwrapper/services/api/HttpClient.kt index 809e3ef..bba732d 100644 --- a/app/src/main/java/com/matthaigh27/chatgptwrapper/services/api/HttpClient.kt +++ b/app/src/main/java/com/matthaigh27/chatgptwrapper/services/api/HttpClient.kt @@ -67,7 +67,7 @@ class HttpClient { val json = JSONObject(myResponse)["result"].toString() mCallback.onSuccessResult(json) } catch (e: JSONException) { - mCallback.onFailureResult("incorrect response format") + mCallback.onFailureResult(myResponse) e.printStackTrace() } } diff --git a/app/src/main/java/com/matthaigh27/chatgptwrapper/utils/Constants.java b/app/src/main/java/com/matthaigh27/chatgptwrapper/utils/Constants.java index 67714a1..05cf084 100644 --- a/app/src/main/java/com/matthaigh27/chatgptwrapper/utils/Constants.java +++ b/app/src/main/java/com/matthaigh27/chatgptwrapper/utils/Constants.java @@ -49,16 +49,11 @@ public class Constants { public static String TAG = "risingandroid"; - /** - * lading text - */ public static String LOADING_ASKING_TO_GPT = "Asking To GPT"; public static String LOADING_UPLOADING_IAMGE = "Uploading Image"; public static String LOADING_ANALYZING_IMAGE = "Analyzing Image"; public static String LOADING_DOWNLOADING_IMAGE = "Downloading Image"; - public static Integer REQUEST_PERMISSION_CODE = 1; - public static String HELP_COMMAND_ERROR_NO_MAIN = "no main command"; public static String HELP_COMMAND_ERROR_NO_INVALID_FORMAT = "Invalid Command Format"; public static String HELP_COMMAND = "help"; diff --git a/app/src/main/java/com/matthaigh27/chatgptwrapper/utils/Utils.kt b/app/src/main/java/com/matthaigh27/chatgptwrapper/utils/Utils.kt index 826aa02..daed624 100644 --- a/app/src/main/java/com/matthaigh27/chatgptwrapper/utils/Utils.kt +++ b/app/src/main/java/com/matthaigh27/chatgptwrapper/utils/Utils.kt @@ -4,11 +4,13 @@ import android.annotation.SuppressLint import android.content.ContentResolver import android.content.ContentUris import android.content.Context +import android.database.Cursor import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri import android.os.Environment import android.provider.ContactsContract +import android.provider.MediaStore import android.widget.ImageView import androidx.room.RoomDatabase import com.bumptech.glide.Glide @@ -84,16 +86,18 @@ class Utils { * @param path local path for converting to ByteArray * @return ByteArray data converted from image local path */ - fun getBytesFromPath(path: String?): ByteArray? { + @Suppress("UNREACHABLE_CODE") + fun getBytesFromPath(path: String?): ByteArray { + var byteArray: ByteArray? = null try { val stream = FileInputStream(path) - val byteArray = stream.readBytes() + byteArray = stream.readBytes() stream.close() return byteArray } catch (e: IOException) { - e.printStackTrace() + throw Exception(e) } - return null + return byteArray } /** @@ -220,7 +224,10 @@ class Utils { return contacts } - suspend fun getChangedContacts(contacts: ArrayList, roomDatabaseHandler: MyDatabase): ArrayList { + suspend fun getChangedContacts( + contacts: ArrayList, + roomDatabaseHandler: MyDatabase + ): ArrayList { return CoroutineScope(Dispatchers.IO).async { val originalContacts = roomDatabaseHandler.contactDao().getAllContacts() val changedContactList = ArrayList() @@ -234,13 +241,17 @@ class Utils { contact.status = "updated" changedContactList.add(contact) - roomDatabaseHandler.contactDao().updateContact( - ContactEntity( - contact.id, - contact.name, - contact.phoneList.toString() + try { + roomDatabaseHandler.contactDao().updateContact( + ContactEntity( + contact.id, + contact.name, + contact.phoneList.toString() + ) ) - ) + } catch (e: Exception) { + e.printStackTrace() + } } else { contact.status = "nothing" } @@ -254,27 +265,34 @@ class Utils { deletedContacts.status = "deleted" changedContactList.add(deletedContacts) - roomDatabaseHandler.contactDao().deleteContact( - ContactEntity( - deletedContacts.id, - deletedContacts.name, - deletedContacts.phoneList.toString() + try { + roomDatabaseHandler.contactDao().deleteContact( + ContactEntity( + deletedContacts.id, + deletedContacts.name, + deletedContacts.phoneList.toString() + ) ) - ) + } catch (e: Exception) { + e.printStackTrace() + } } } contacts.forEach { contact -> if (contact.status.isEmpty()) { contact.status = "created" changedContactList.add(contact) - - roomDatabaseHandler.contactDao().insertContact( - ContactEntity( - contact.id, - contact.name, - contact.phoneList.toString() + try { + roomDatabaseHandler.contactDao().insertContact( + ContactEntity( + contact.id, + contact.name, + contact.phoneList.toString() + ) ) - ) + } catch (e: Exception) { + e.printStackTrace() + } } } changedContactList @@ -318,6 +336,20 @@ class Utils { return jsonContacts } + fun getRealPathFromUri(context: Context, contentUri: Uri?): String? { + var cursor: Cursor? = null + return try { + val proj = arrayOf(MediaStore.Images.Media.DATA) + cursor = context.contentResolver.query(contentUri!!, proj, null, null, null) + val column_index: Int = cursor!!.getColumnIndexOrThrow(MediaStore.Images.Media.DATA) + cursor.moveToFirst() + cursor.getString(column_index) + } finally { + cursor?.close() + } + } + + companion object { var instance: Utils = Utils() } diff --git a/app/src/main/res/drawable/img_test.jpg b/app/src/main/res/drawable/img_test.jpg deleted file mode 100644 index d15f86254ce62357821ac0f6044fbc62ef97f495..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34245 zcmbTdRZv`S^sd=x1Hmmw;}Ak4Kb*$Bad%5_4em{F?=yA#|=aCdii3z~$4kjej? zsZ(<~GplOvo4s$I`s!QuuJw2I?*j1~CJVG$x6b1(4C5ls7b~5Q|Bsp^r>^?uMDq(jyEThx|1K zB^ApX*0*e6egQ!th_HyPoIF%PQAt@xS5M!-(8$=z+Q!z--oeq+%iG7-&mR^Z5g8R7 z6AMpHNli=7$jm|(6_=Ejl~+_&H8wT3w6?W(boLJn4h@ftj*ZVREG{jttgfx^?H?Q- z9iN<@o!{QwKRkZ_@$=~y3IOeYLonJUlAJsO{^sq+HwQFW$7vQx8T6WyO4a0dd&;|1#e!Og zvu+8Az%qohqJ-JuKoP(1|!Gc5a+1p$gHF0`B^V_54~ysNhK@Z*tEkBGw@cm1m^ zLBf%ISSh2w%V?C4ZSB*owYi=u?ybt~va9F2i2KpT`i=FP!BGR(J$OlrnA8b>=A1Q@ z7xhPxlW&{{j0$Ea+B9F1r@KxsM zAp{xYsKFOtDL?+=zz~LM{XCPoPx(0Goiy}hz4AEKPTa4k&r{A!;d@}0x;wnAN(tay z=%_bNxSbl6_>EyUYy$Cn`Knt;X5k2A*_f>3cAD8+m%}b=F77UM?J*DSn^q7$4y7DX zUI^PXO=qQX)G{A8MA2C+XGYkd3%d7S=$I){kAz@3_K>Boef19!wf3z-!sQ4?6)K0c zKx0`SgiG(S5Or^R-_#jx6bru3ceGEpC3NT)L}NCm$~z^(? zrSz3I`CA=IMwco)PVw(&Mu??cXv;G@AY+|FWR z1E7UV5mIIL#k5%mEIME540r86#Q|i|t4WA^@72L`a{Db=LQ;dSkhdK#U3rMGU z9#t;pYtW&tiJBMyYswgt!>82TnlbDx>2*ueGM_7dO{sX3GH(qvX`{po4;&J|% zRwj=yFMr9coUm<2&;lK2{N`-?A1bxA2saRaz;*I<&m%AbbwW)GfFYd=!6F&t_YdE_C2>uIX8sm7dTo8kkz?0@m!|12+7#$CAN z>+PY$`E!X$*wM>ZTZPfp&!=))lPEC|>%zzbEJ2#BXJ`66Jz}F{5;yJXL7Fl%YLZ`T z%(u?%u)gmfvzKL#gZ;1{=EF#yJ}1bMJ7Jal8nwE+n_FQ#JajXEK)6E+ zV*)NBJT?9TRIusW;`RHVyTT$bE>X%YwlXJZ`oDZE*FoGBXpZ03^A>8tJn%?L8baR0 zE~4ZNQ77fAr%7$bSex%2R4~B)@OmoRw+T&+$BR3HbU(Qz*OC5_XF#h3uu@dayz5?= zVfqXBtlEc}nfW6N$ymG4!*3Fn-898u`dG3#&Ml(JD29Uk7$Ey`fH5DhYuYUY>DBs~ zV27f>s@(eSi_Txbc~!BC@P*2-Fn@1ED+T}uO@i$Z{T;y`61Jw=+ld>hXG*gwZckVp zo-==t$5xals)-&({MuK5!=%j_*IniKI>80c!Qs2R5~ZCv9>7+mgBtKyRi`9S>=l!N z7oNy2Aw$`IrklhG=g!$r#Yjxi0xh(Pa4GICkPT4+jZ_<0?&XvF7mAjnEt**^8!z;2_F-V^p3JUuUeIItc3f(UrUX` z#w4TtJrmc~{jaWc0+CltyyP8mKp95h;|!g#HJO!3Zn1Ax-r$4etX`UuPkPz`YdsTv zFgTp5WU?WQ2y2W>X=X+IT(T)LKII27)76VBsNVx^YnVl#kPif3;LzeU@?tNQx zXPS`MifR?_AXL}$nq9iKZj6zFKJ^j%dp_q^&?8-%Zu0a)*p(~D`B~u#=kQm+!YZbs z^jfA}66w{(iJ<4jj0P;s@Mg3Zy&Q! z)c8zpNQo-H1}=Q_l6b(HGH9~9J21&yU_G+Y`2|m+q7;r~UI(&dTAi&3xO~&%8E9sy zG2N}eGLb02_p7P&rj+vW&H6!?TEL#*KEzmOhJ&EE^yrD97}AI06;pFYhP~Pgis&-psDOQ37^79P!=oO_zKd*L>Lgu ze(>mcZ#)s4C$lwWVzfGNbO4VS-g1{RjKS6ELG>DPCVA=?Wmu+dg=Z(h8 zTp&!bPMI<|T@npPAS2)FP*RuKBPK0mZVmVdQ`_Aq;pmKWp>bcS39@$S}WZB)B(#9ssy>x z&|5!YEoB9`7|D_>5``=J+DSCy5kq!XTI^>0zH2tOV@d@)(EWk69h+OZ(cC$qnfekp z-iP^m&gP{FQR+Lb*=3a_t#*OZS$71C>L{~agD!e*x<} z(YWMnp>%u{eIp$}q!%orn2)oBuixN!oq|vz1XU7e&?Jc#%Nh$-1NYP}{ng}RzY62` zNB?ocZ_4o@*tOx)grFAI*zb1w{E=TIdZp>xx3bMgn7rf6N5KGaR-SM;cMkhsK&yno z%&8-j2dXm>V`6YLeNe7Fnmh+5nJni5TD*}uaP|NhW`Z@4+s2eKlEv_cN0W1DG8!zH z^k-01W4V-XGiTRDTg0C^*w~Uy&4!V9+di;I#={Y^8B1oY7OH{qwZEprYM?M8;d{bq zaADQu<&%+=r3fX2Lp)}`q-fE%5qd0)%_Oe&9=Ajv+<4Wybw&MW@8OZmas6l?g-%py zxBmE3sNZ85vc)z@*(8W>LxA+QYP=6$o`=Y?T;Ga(GeFmo=b;kK+FeyHK>0S_(Sl*E z%~gQk{plk9p=c}Ic|&SE+i|Lf;c&nW+;42*4_b_M>Afin<(aXramxkgeBb9dykj>O z;AXJ>;{-lKOs}4)@5?iK&X>ls^l`9#qMYWLFWW{V?I)|Jz>mjRH@A&c5P$%@OTBjDib5LRyTbHjCP{I!tlr6bKNL}tQ?^+#)>1a| zMpEN)+l%S8@x|I~Vv3&=h^ct{54s z_k>9+ViKG9z4sG(9$CNDyx3;m#&3mi^CnU#0>vd~BqJ@H)ZbCiU`euRD+p80+}Ao3 zCY$IPS=uJPVN8?8%^U-d%%0o`g!*#aRJ5d#;eZO>!G13`zZaN1nGWzdgH94HP*kf} zzXRsf_X{q}yg`yMJ5xe-agt-^IY**sbCAAi6c>Vum!Gt2D{Wa)pL+tUemQ)Yi&9)0 zp&YwVYAE#-6fe_Af&D&lkvEJ(VM$Z$+bx%jbNCCe;eVBZ&czpb0yhg2V6id#l1WLY zrKAWlqB0d6$jpt|P1nSJ{4!$&Z>R+6Il87xDRPVKtM7%_5W;bmyK>aq*;ET|sWXV6qq5=R-*s zdi8UGgbhJ*RCVR~%=x=f!!7VEYV+j34(o4S3Ck^6wd>+d4D#ZXZn_Dlj@95Y-LIJi zZbAeXQW0%CGpXk_K*-0%GLxnETaz_BVWm=43|Dd$8xcX}pzxo1k)4(`)gH`N*OXQ^=9<0^t;;+H&X3w#ZYV09IM@NZ$pzQ14o!Hq{hb&3eLW* zm+)dbPfmg!fjNt;xFhtKBS8=0$x>q9`z{pma)qCd?_y6RT6#P!6E|FOvFjwT5!xTi zoj#X4p6+!eU(-`|>5zRA{2r#*7>pCK(U}~en>5CVSG7uJE@AO%*dfNI^U`AV_wrV= z3Oyxi!yQ4wo^`dHgo%10VB#tH{{O z6>2bA^ML8f0oC8-`ns1+yD(K#DS5!qVZxZu@RYob*b$u!eC3HN%4lV|+ zR`PvJBwQll+MFYxrHl=F2$@oW^VZw;OA*B_g2}gtE8%ZC#P^A)4-XY)J~>3;-wboD z!?e1aEQ{OInCbs>jE&=_btn{6?7lTloS`zgvItL59;cBv+*TaIm8+z*WyS|jawCZQ zlly!}N#W;L&1rsp?5gD~r0z3Bn^M49B@$*aao2P5SB{Y(b?@vZ^2Z}&6HQ0!o{VeT zmzrZjXkscO2pe6Pay%U8QfyF=4P87}sF?uD@+!f81!e0P;u_mp$s#qv*HRQYp7)|sK8ctfv~Chq`tg9aj`Vq>)Po=AO~J(i-z(3s{m!-UaXiOH z0hEY&iL!#)_}}9i*Y~I~dC?;Dz1WxAkszo4rjiA`fzgy+=M?c-WJCtO1J+GMNjqn1 zQw?vKos_En0*Zzw@5HWGH<^2NX%avu6;rmRrhm%zax;?sW=e65Xue0%w9|kAU9o`M zoxC(Iwe+II8=8>bQPzLuTJ42FEyo)-BP&SnnAQ`{{9LpDe@H;g;HV)0C>`Ijb@oe) zNSX6vy#?Y3Kj;ZEb0}3~r@c-slRM2jA=AOvI2wWZq(rw3N({!i;PgssY4!GNjTanu z)4N~Df4BWOzwS^lY~@1D#07-XgI<7S<#^eQj0YaHwA@lmD`;hW(~?~5d3w}#s%*?2 zaeHB{PB>h5q*u1+E;FB>u8i*dbpI4SuP&JdoxRE}r`?i=0U8u_{c+uRC#aJR;{9gS zA1xtPt~>rFLI6{MqC~jFlEhrbVt?5-xWO3F7_!Ll6unI57N%!74C>#VRULVR_VhRb_2zU7k~Arqo-%0EMFX*FtxP%)r*+Xtarnj; z3sKoWKgXUl+AFM~hBtcf>pqIJ7qUe&BW#Z^@1R0f9hmi8Ovz*QO4cq21<2K3z(rjo z|FnaS{UmZ`3OH(M{Wv;6piyhrts|#MUjug!3%c^6hzV!>Z`{v&J^7gtkMCE@$$NrdF&LhMGfjU;?EiidroAyqd`aw136WR~xj;=u`Qy0gF0s|8 zu|kH|utgm8q4QDVOY~h`m{6;kBqeo&ZX%)tBNNRe-OAb`(;R0cRwKvyg{tXIS=YzX z3td^k3{6CCVW+l=S0fu>MF{sVpl3{ss`Ce6m!U9n#0)KuUA(pO_;Z%DDQydIb=@>+ zzNAS|Tg66RkWgD{^|)4vG7g@%n1u2b33Q5x`&bmFVj_jt|14%{ zfY&c~Ka{j&slXRe6sJ%dl3d+ssv5xf@^+guC(5R{ID#i|-PUw%A1-&J;=*diCQmgq z%$nvPBk;iSH8?=arSkXmmxobsL8Uu9SwAlGLunLwR`0viETs&u^uGWl>W;ea>(+*; z0S4GKHLVAeXyrZX$P0{(D4MDylEt$}!Fa`Nh?oJ;EvwzFy|?=Af>`M;XLtD{LWx6k ziIMtL;TZB{c>6CYjP^tcao;WwW_?qpq+Et|rJ6iA257?o0LfG*_k7@5Dbs8vL0(ug zLm5C;x;R~=eu5X`yc{BZD*7VeTmpHcz+%v1y{&F~Cs>tR{Y}xnoN0njnCWm6z~M%4 zpc6rKr8sQ#lUn1whX$lX4&&1}Z^K<6pZ04##P|UIz=V|K``K9xOO^3`p1c1hA}J@ve?G@2K)Y`)h@E7o=j&0rdSx8Nd07WLC8+xkx;iTq~KES7djwS#R8<7l%xb@?0F8Hu#F!+WF+y zaub)jr`};`>?m{K+xlzwl}=;!gK=i_v~?KmF3fJBEEs3_%j{mBAbvrzKmE}6T=b``Y}{0AZ?dOZ`s5Ed)J1D8L{z9Kbjt1iPB5NG#wO?KMq9Kib*3~uN#(Gs zzSK`}!XKmxZB(boExFAbF3n8JL8R>$a^SQBck-4>Cu16wpLafjG02+D+HwAdkTxX+qS@AmM^KH&+`N<=6#)q7xcP?775Zc@!@794S`L$WZzpjBX z(nohSs=BHv*f@U_483-ow6EwG(i^qbG)W&8Sqi6?gYLltJBS1iMrV}M&#iN%w8GeU zhR9exJ_uNF8Ib^%*vLYUYu2kEqEqZh0c%-y7w3^4g*G}tN25hddanu(pZyT3yZm?H zV#v;vx(--PH%Z+AJDnj-*3EJEi^cHyq}-Fz41Ylb*TQ0 zC#aX`3S)Wvi+vyXQj+QuwUEb49n&YR6c@mht*Z{huGQiKbJmf^q=!cByB5U~(^g&> zs)!ER_*eh-+L$?pR&?_`9$h-iFHlLGV2V+RhfgN2hYl3TnN88=8;@-`(}n{vOmbp5 zqlM}9{Vw1RghLH-yx^V|AYiYXZ9ilmUnFcX0AC2kL=Y)yi>c1y?YD}95T>Qa2i{Vt! z-M;(DZHwLJ>EQWZ@#1(Z#J#~FSp@$%U>C<=Vw;aJ*!PZYnrB1ej|LgNwx$=yw`TXJ z{F?$f#W?ZxN#K^=OwoU?M{0{S$PesSxI&}bAveIYU&}RfjPxbi&i^ISDK@oj zT85psvL+Uf4oF(FQT*sI~rCSL(&W#r?cb3R3O9UD>qW?>!N|^%U z*?xOK00hboPY$@Dv&hih1oyzNeE5dG)y`V9X`G~4TG3_P;%pHh-KmxBVylF~dmYR1 zZ=roO>{P*50IIzG-_x3Jt@w`U zBwHBc_jBJE&QX1@tluvBu<0Nt4~qvskRN40$~Ff_%i$%lUu9wLRQyO@8{>y_C|TdiF>_#vS2lE2?B1Sfi%(1tLdEcxsL)O|=0jLC=TY)7-a2;& zdBF$W*kn#>Rrpn#KsGB!!r#mOWFd8i!CN7B8?2>HBlaoW=HPk0l!h2iYDgtUwUw*m znSmPR@8;X`3Ob)`=*M3UIe(+SJ}81V_MRFF9y$@qwRZsZ`#v6)9j zKxuU9JIi5)6HY!nPZ1uMupfT`!qq%r%-<>ii)fq>f-f4RPbM}w@LVZyQT2{8I(chs zJGZ9K=Q=1`sHLw1XW#JM$stpj-`Kx!oJNRXUfb;=8J3j1(5cXMKFjAq@1=O*3a6_q zmnv01VNTz*zQcIK?{dcDVy?tYEPAKW0)wUyhWg@KLA1*m654A0zr$&U(?Wh}CfSmE=voHezeXn^11#D5ESl*+JhVFH6FMSHMjD-itFwBmnL}H!m>T8M z*LR_62jTzRSH?%AJt6nOZwe|+pWmNj!b;P_Mh*Ej-yp{;1pK8Z9Hkz}XTba}xW8J2 zPF=$|U7N2g&RFa1UPE~Nquz`_(d};DlJY)hx!WHwhkhib@C=#q8A?Kuf$3gH)e7t3 z31UHxFQYp|Lg7hZV(08HfNT4!U@*?*b(Ac~gPr`G`w89aQ2)kGT+aXM5Xzoh{!f?T zD0Y3fNwu>4FHH|lTJ$u!qI}W~Tmc0L{RLEY7FSNN*czOO{ke|b?xaDFj!bdeERTFV z5tzx(4&&Z$d6pF1Xd;4ej^9ozl<%pgRLLlw8Cyb$w)%#w>?p(`iD@4*8&hiGzC zOc(cv0G5o2cx-KEwFg4g4imnkqvU6#vpZy;aXM-8LTKfTPVreg4=mzq+u8N=tY6Xu9~-5F4`Z z)Oz$QPX)6!5Qx5<+Ipm>pDb}waNGn%lrmZqSC4J!6=(|hEzU$E~hy16g4_P9#qd)^Cul{QjSXy-Gf|53U|0R&se8+};W0vKiy|l-l z?FBs?jgcnu$&*rcOwYGEtTrA+b(cQxUe~9eUj?a#1X*gd{RPzZ7%wl;$7)HT1hPxY z&RHyQYAH+}gt?m-_3pZx>ZGl0VXff;4k)G_&Qare@*OSQG$s|%Hfn9Q9X*wgm&XXY zGf!?vi;1%ZSbO5v6tWnL^E*rKc9f@5bo{BYYUaX!yqp4my^tmSiSrk>^YwP0n?Ma_ z#G@u(;G{&JYfZF^-EIKs3Tl)2K5Tu`(mos=Ck>#R)%#g$pmu0ZD<$p}ouOs@p}N0r zB=ZOtUs8D{Q2WKl_05r$iw}km+T*EXjfyxAGrl?&`|5Eiy;ZG0_wq++r&k*2!ST(cULMYio$!lZb3NMMw%%bkSg74dzX zz4jS(cO#*9j7p(>@4oi?lf`j09d|ifwZrrRnErEgHqB4XS(bYOeW$UH);AiBSH&Db z_u@~k$Ggy=axp>&vz&iBq%7FmluWT%{&335zg1bm;>u5yFUarb3`Q zuF6ha{sluvh)0_2<+vwP+X|dUnuvSFCKrnG@xrd9j~4Z8WS0Bw*i(v)IqrJW{r8GS zslT-;Kcjbp7Levn5|q=Mn#{r%iX{_qtfl&?E@ML&_uJ)d5tCT|+QPi05LPmRZ20V= zb9ab;je=GCy=v~Dxk-}DHIP}mWEYo6W1D%;9z~G@NEV>hlB|-rr!>S_8m?O9>uDAg zTgKt&YmVe%x95MaMAMe4)d|xFMSGRx8(&`6%rZPVrFJ7YYN~{*)OU;8I82mq`lW}j z7wb(qwCw~^neEl7E6Z(FU76t4&0cfwn(9%TKepPpQ61HAWe1s*y3(l0nRx12&*|TR z!W6WOBzc9H$O>6z?WX-o756*ciphAFkP33zIUGsPJx&seqagTc-G}rwz9H+*YGvTf zNv&G+lqb%MAaiS5Dgr`eR1Sp2TDL8qgfltg=fS8zTOB1!BdqZOfEhYt4UXK|-I?hogwwOt(3SNUuPKCt^gU=)}0wbeDr;mnzH_RQ{|Nkt~=w}0U0 z$MSpax_u8~H;xfI$0Q>o?4L5pxr`kr*M0B1_e zpNom(pPy^Ed>lHhr5^C7N1hgnekSc-L~S{jnkbid`?&PJ2v;2iDt=E2Fn!RUTUy~N zg#Wn=@zk@KXT<&jF$m)J;H3!og*=qplf3QK)QF%6Sv?#$P_6fJw)M2LQ&U1<&7+R0{xZ5!#LoeH9-^TgJcJidT(U(25P)w?&~z{`14N z@Uw?p`BIt2KZl|s=Ahs>-S&m0jL6-bgxg=b%jwx3uMvrEqDjhU8Mg7EEVxR;qBv3h z4f!q)Z?xR}AFEy`hqj|3=BebJy6Ul?ZbNe{JMc6Hd50$(T9*!U@HOfmD83nZn+)Uj zxhbi&|5_1Z$u^smI?|}v&v9PN5^CiA+~PE`OixVGF!R2@DK2hkZo2HNw(*~?qd6k)o_Z?mx{#tqM;xp6sr^;*c z{NH7WMoTU$Ycy}06SC|tf+*n}gsX?!r}52li|I$%6+TTgzdiJ+7<;a2R-Hm67yjT) z#ySlJ-c_M&_>b|=7tlJ4Q+QV#GS`=Ax9=gs9PS#&LW+uv(;*WN1y%_DU~SH#C`$h#r<8A`0hnjWn6eU`)fx+UX`vC$@wPLfOsc`kCr>YI&i%Ed^V93 zZi}HR{RKRlj1e)b1bY>G&cJv#d<9xhV@`{V7en7v?^txQi*zRMwyJ=%e z<}?B6afgQo0hx?a;{X$!TnmApY8ozO+A6ZR{3Gs_3uaUI(HgwTnHimqJM^)D%d}&u zB*!8_xn>3ToK#P#H7kDL?tA5)PP@*VWyY@xzbP%AFEUe5mW4h*B@ECmh9#*U$QPE6 zM{rQ+$?1R8sEO_^f34{V@Y{=8aGPwyhwk%?hmQTqYvFZpTq|KbDpFuQP7nRT?lEXi z0E3f*l^?m&wH_dK+3$V^fBp%Z9dk=kArjtkS?itV^+2J6byQgKa-@VOer3+O4hW9k z(=5pwf(Ud- z1Z#b2C@yEKUM+!>LANr|&|2B;2B;`k9<5v^Z`TLAj+3}y4_ep3i8cOK>HW~K%eU#= z*!1Q^A)Y_O*uUPSs+HSUY`EPeNN9^Tc|B*KHdl%&$bxkj(tGfe2L`t3W?uqKuqMCW zFS~9F9dAj3k>AV&PlG=uF@7Hf;2b~V>7Vi#7Grc_is_@weS4%%au2dj#yzY;g#R?) zRoBq>;S?17_A#)`IA_z$Y$or9VdH7pd1R1=CD%7j9Tbn-kVAB#C#K`L{;s(6+*p^L z+3n-1{il%L6WpFYkO5`Ixv%kY{g1Xu?2`|UL5k5obYZX`tSKg7&DNTs#md0=!_701 zN$V;TjjkprT2P>4#5VD4-3+&y%XWiO?=!x`G8M_l-h^EaR;jSxMlm?0w|d#Z%@Z`h zTQDlQO?P0V`-fIHBs`jcwnyk7x`y*cgJu}LJz~Ye*qS;ct3T?B}pH<{B>tA z(iV*lc~fnlr7!*hQ0l&^L6h~?asp`cTKb-4Z`K_|5U?Dg8lM#fVrX$>s`tps=M@3X z5Dd(V3Fd#0PMx( zopTQ7UYeuCw!0xuFeO~Bm}?7gt>*I%)KFh9?ao0o#)}x^4|mh}A$|5ZL>$4`g0)yF zXtdDnbfB^;+4!L1t0}o0nALyq=h%&1%IPnFD3kavU@Z~jP;FL~|#1-M)!{K2f)Jg4PhYu|MEf%Ye);Kh(EyKJUv=EDLzd#WX~&aZz3R-DRY z;x4}@e%03Lk;8eC*xBWp@;9C3gH4wY2O~a2prD9l6Gv37j=iy&?YJg0><*5#-yIM8 z61+lcJ>Iw-F#8^Y!V`ll%2J`SssC!}G)KYJX!c@}k2Z?J!i**L2{YYso`}2_ zv^&)JndKHS{*a4A%;8!8EvfkRS6K67@970GrOuEI6rnmm%8J8&)zgT}DS@yY=d$sb z11eEsaP6RDak&6DZpnU8+7FO`k~I!g#8Vq;NQ?g#E2~-yngyCACx7M1w~L&0lE%Tk zS8H2)%~+a#;5Woeb8WLZvSMGwBJC1PwMI`Srf*##)Nm#>hdVc{nY3ILxnI7np`Jz! z?R_X??CELGBo4-L8k{3Z^pi-HWrIug=ICX|qEDnz98`W&O_z%JUheP8Fp9n(6Ax5g zSh(H~&7U$obhBs4DqB~J3{mk!D&5=KgMEHy?d%sK`XaFVE|)z=Oh^puamnA)EnQ zo@LWY+>QOrj7$z@(@3xvr*NicwjrC3U$;TzMJ-^;((NdoiW6@}yot|5m*%W6O24oL z6|TY0&Lg-5FI>{nf5c@@S01>yPdH_t<8+oY7XLzO-A76Hqqipb)4KAMRr&jvk(~es zf+nt`nf;O=fv!E#&}u(crB2rW4zWrkuL{}!U^ol?Zf~Msxnf2%FMKk^bdf&pbi&$& zW9X}Ir*ZalRvvjKIQD!u%!L}<)uc^rC>d7rlWRkLQYasK=~wtF>_tAt9aC`r^YIaA z0t==NyNKe(W!+z1*ON01HTM%{l>Sgvt@1~US((YB9Aof_!NV_?Je9? zm(n#)JI=dqUW@Mih&WiPJzV4>%uIrp!9cL({5UaDSBDY}5hy_JQv71RS)7~`IMBNO zQJMQw2Gh^XM}aB7d4lHCHg}R`LCR;a4eOMr;lO6YKFm^RhB%A9rA*ueO>E*9aa!nY`~E(7u)m~PzXP=)jsby%Uz>QYx3c4SyScSQ#`yk zc}EvHbv{WjF&%ipyFRTF3jjR!N!EIIIv1_W0frTi)Ay{5OkR!6lsQhiHP!8cb3V41 zg;|!nI=+(QC}-9)3dL+LS?5*I(S4tBXs!LWIvLzA3J#xwo};;|Fb`S1Y7JpE=_bT8 zgKihV2NC43&c@W(o}YVPqyZ~l!4+!DCCzM`}#Jhm5aw3_^hAk43F z8^e~Bbe>^+gg@!ImjpiNWH`@peeiFON`O+pk>8G<2U6Lg&ve(ZpYWf6Lt!64rzo1Z z!X>&#GRz;#cJ4HcD)h^DZifP! zCbtDzhh1WkMZ$Q;zCDLcd4DC!3M(I6sdClcl zayGVCOh-n2M5%ZaW9dr7lOD9L0_Y)I zJR`G+6#?&WZ+!PyuazaDR(YB-*V;$u^IDE}NSlj27?Av$dN}f^ttzb&w|@aRmt1za z@vC?0uoYp?;tBtXnuYYFg+!%kcA&VINDth%ODsUW?Kl4|R1`1g5h#ZI1yqGJM6V0H z=JwGIaD_$ z?|sFm$Kkz8JlK8|WA~4Jq44YLG7S@Vkgwyq2+di`E~QOMGK;!^mL@eLXn^H22_PuprO{E$JO zLAWWkS(5}Y4+fjol1-Mx9BKwo)v~k)?@`Ve&!zL_p*&X+mjnjjM2*X{v}pQMqUJIf z>fnHlm~}C7BGI})!6L`r-zd(I942*d(9I9*I7a3a9n_hX{vSeyMM)(3X#XXB1Ang+0*QtJCX8ZI^b8J6Nys&Vi1!P&-M=V z!?UFAUU3_c;l?u_ma_T#$Hlv$Ra|o`itp8)N^gr8)TY%vXm}>=$^xqsZwYst!L$(u zCTu8@I7b4r#;gY|mfPiWmVP$lG}NZHZbnQVzk_=yLGZ_1hItbcpSZS<1yM9umrgOa zT$wBNl$Mlvv{H0~q6ww%Zpb@GTL)AVFFQgk$T~;!whzk0F^r9$+5 z&)|~Qs!5f%C+ju6eGBx9h_23M(D1c5LW%#UN05|oRpVMYJ<5=?a@^UFb{0nQhJ*MPyvWF&t&L4bVt-@3Wd9%Yd7mg=2!p$~02jss`d0kO z!ZVb{V&q?_=I^s(Edoy7U&kuy%{*g@y$DRp+tR5cb%)9I<2+BTq+*pi)_abB`B zNK>21d-ZvKEH>5LGK<|7*?6@(j~VSBWH?$TN}}U3ew;br)?4;Qo_1dveHmfrWmvU* zyL$j{uWrqR=ai(G+tI<2s75GeXHNAdg_KVqr^UHynjmR6}Vf76f2^rI2j07G$46Rp{3njs0)uGd?zD< zLzvA5IytV^M13tFx|Z@5GFrJ8Y4qKzN)87hgOe`o2AiL8VMN1Q{^VrE?)**l7579s z<#CUdbe4-(%ql27*7Fe!v%7)*j)EQM`kBqgJhspx?R_?F!GS=U{WdF<%CxD?v%E26 zjQzi$r6P`bEW^-rrgQ;)@mZ078zqI5PyV}09d+7@*ysnK+xZT%8W^$5N#aMt29-b) zwMBi&FSs6rl3ihvGQr@h=0>V<;5$UfAHNHuJjn(#6XjN$54nSt?=^+#;v@%>$!D*0 zQ#weQFfWGWw&qEh9Qeu*-yf>olNF4MiM9+#LU@)_jnv4sb2cByh(Wl>ERp>dsOs2a zvnY+0Vs~R}$Bh4;0n@4C=rf)hEWf{pr_8^G{CMb}!k6K%h<1txYX!obDlXuKX5I#3 zA_sjEZx?2My^isw|9YkTM^dC%Fzq1n>AhLE&BMKM&$6!0O0Au05(p9KXo@^HwE!;M z1|Rz+-h7SWdjHxu^5CR`w^cIN3q}yasqdZlz&~03bNbujz1d*jt4k^DYRzGCZoseQ zck>#RouwGF$?K4Pc{sCf9F$Dg{OvEjLE&>SMe&G4Pt_*djDVK3U=nF;+LUMKplG2j z$PRv9z6vz%TTJCw&730Ew;tNO7grWk%0xlLvm?LGCg1;I+(Pih`^F1DOXm7gbpysb znDcUcY6AY-+qdgkHMP4; zWuqhi2D5UN zL#kKHNC#e%I^~gBk=oGSkRw4OV~2h*6hAY)rX(}lJRs}jqqqdhYCVPIwU^k~l0LNf z14$p4KTSQ|>JTdebjX*AJPfrno}yc*2(+eas;78`^j$M?{BxA;PrEdgd+1w#8^=x3oJ=;*feVDpvWO+9%S2LSThRp;7z&!c+L0=r?2j(4;Va# zoFypuw@Am&--PhN{X=(1=~pMAx{;QG6W+(QMv4s*m!5FlkgGA$2rw0(dFps9M}IlP z&$&FV??~}k-V^gA2M4)*)IB!?qpj-AZBb}2a+W7qSESn3O3vETXfe|zpwt?NmGvB%czNn`gl}&0CN$@I*vSTY@PdN^@bp5;*N3c)D1tE9(#W+NvURn9xKs2w+li4brc8& z-0Np?v?aCGIg~a$8~&*J-SAD#0WzgGriEPsu*;zu>&gHmDwGm;$BRNA5?!WW{C$GxFKzkH78XBd{HTGLmXch1o@EvjuL} z)&DO5g+O}0dR8^|prkR+IIMe}VMVx*mO@9}VaTp?Q`Z$>X$uqRtJKrnE~1ONh1_M2 zJm>SQsTcqy7z7;sMRD)q9YODqHL9cx_`xK%=hC+>F7{fUM)n(2cRux2GUmHGohIXQ zuOp|ebP2StTH*958)}~TuEGfzs6265JsGmq79Cd@^{mUdfga+nyqG-otlPP;2%$10 zV&0%qeX9<95^-ABl5S=-k164eYK$afU)!6zs3QmEQdx{out^mMCrRSalVlnS&HW{G?;+ zS}-E000Ekc@<7sjgA|d?<*zigDFy6!Oy4auCeZ{>vCkD~ zTt3r|1ye&(hR+lTdd3~{)h)q|BLN3BBnSsue2{{qiV*B(84r4LNuKoCzymc|B*Rlg zGfp?=uBpn_bW95S)wOfBsFpSbDn=^aNIZ;G7SbqiO=uY7mOO$pQ(>tQCAqjb0;0LL zmeg$-=Cm!3*+%1n4M}k8b%Tlpxz9qbT!Y-!pwd&x50Xjur8c`8AahfzNEZb0R7*v5 zvf49Jc?1qcYFWZ1Uzm!H@GM#8ixYD@Xi8Pbb5K0JPHME1w(*0-4GhywnZcxDSMKvw zQbJH3^>)~AVY-?fh#F~CPDdX~&zj}S9M?yA6kliK?_#-&#KAcAsxxG3J`Y;avIa&5 zu{N-3*RZ%qz|8_q=T&W}%cqo*A_ERN99L_h_>S892`dPu;1zC%>0W(q;^1UuJGCMO7-n}@e z^9vD@DwX}9c;6}azdA|SZ(R>6eGs$rkaLXpso~LK4jGu19WraMxwAm!agt3^YjB4w zy|GW4b?(Q4Dgv4Ni^&jMXy08kHN7&omC^ z+9H@Y=M`Q{(5O^knu1xpyKoiy`_(LilirZ-B+Oep)JQ&Vl_6F=dQ{f%7GcK}>_aSC z#9e^F#cV7wDu8jHtyA+NwTmQ-)_j_dT#(#V63NNf*cEElUT{rTw;p7e1179_dmELc zW?V9{JRT~{-9tAPJQA-R3hU1!P17)Li<+0dm zr1vILMG~&uih)65aa2hg;~dls3oyk@LCM@{xWUNjNsThpXciz%NgTmkVxtlR7AXb+ z1X3)uJoy;SWzILBYMy4?c&f6o3)Za}NUY-r1FcMAdvp$ZdR0kfAm{a_z3j5C)@o#% z`hKpEhEg~+b`5poVxf-1+PP?nBZEfgGGI0+3$%7?s>qG|xiOx@*R@ZfMlEh`TsB1V ziMi+I#t-zaDQ;y}0hpTT^vmfU*C9{tm=fNA)>Wg;294;{YH6OAV)+1ibggAzM;$Ae z(q|2~1Y@;ykgz*z&Wk-ZPDXlA!=J){aY}k;tx+Ukb4_Ek8eHU5QjiWaQd%OOZ!R;~ z_ce(wq^l;}o_|`qsLErvKAxVn<{E_Bwce^zPmQE?J5AfH z)no&3is5<2Ol?p{;zovFncE#OM>NzKgxWh>d3V2FLG4;5OdbzGT$YWh38yRrZ9Vbc zx)I3SLHe3a8KlbhQj+~@jIEk{&zTsXTCXE!gn~?sjsZ2BCTF>dFYh?d;a2B-A;GA0 zxY~Qyn66ea`5Mv-QyFNDiRJ)cbm>s}plHc-$*9Nx9xB5O z5)D+id|*(9%94+m;+7-NJt^Cl2P5>UY?db6UmbYOQJdUG=jlyc15-#BaSPIzsmP=< z$dAkeZA`AV>F-FcA&($dWz_8>9>S?ISj)?!EA`@_Nw%&rQIJp4fHaHfH|ur$s;`} zB#1X4(q$x7fU!6|D$qwg1tLZff&A-6Et`4_(kkT_wtpx#Dul@W2(E}ax5{x*y|ixK z1s4-XGVqeP#ah$!pFHvb&1y?xq~S+3t)^LdY6%#peF=;;Fs`&YE#u!97^=`%uEyWP zS9dHcBpY#>!MM}P$Vll)Jxi2P%o_=^PTnh_@az(*O&dp)l}hK*gMhn8=DIBfV=9Y| zb52A-YBxZfq4J#k(VeI8IIhm#;1vS6dC=JSmf|shjN!l@gjY#xaxg2+lI4>l757=$US{)DKyxcVq}e3^XXcXz6Vp9eY_+EVsW_UtC`rsQc@+euth-I z6cOkuOOv)N1I<`w3V<&jwVQW72^s5I>v9`2q;H(_-m6O>6t4dOF(#>8&Hy}Ci5bX> z=HLKpXGfL|KSd&7O!E@Qj+y@e^;a;=W}PD+#K3+Pxh#vb9fuXsO?5DnxmxCRk%{S5 zQA@DLN*!}2UMf&R;}xqjM5+}j!{o>UZD-nnqOs|F!HGC&8dLp%{$ND7c> zV>Lry1M64qU^0x>W}h-$xQz9#p5EKbjBWIwyOh?@q@a##S|Pv~sM0;Bnza;Ln>8hp z2*9e>QH`g8SEL(Pib&OEU935wI}X=5cYv9G?|SNVSRq(}AObPXIkdRI$*nCjO;_Q1 zQ|1>c=wSGk*fysWv12VCUwVqdn2hT2Sn$zSVS#P&}a^CRGnqPS}O zb~`CQbsJXm;;*ED^PX#)w!Fx#C~iR_vo$rtf+-b;BC9O96*8VPNC|+*6y+E_>NIS% zOP|iE0~8){%`vjTfN@d|4>ZHyJtzdqlOQXCD?zmdUxNI71XP7FUnXJH)Y8Pp5A5+ObgSo9Du4N`@ zX!gOQlI{Nh-B0WND$|OPY_~*#2gw-Q^{D1K=rLU}jgiXT-Nc0=!*0fE5~Fb=r7_Oa z$*9b{U{hd=Q7~hSbgQCr6K;5{+jhVt^{193(k{Z?ONHXAz{IxZ=}wh?UzqV!X1WRT zB>>rC;^1!~PZdeNV@)n`pIVY8+S`cDQ8xvQlZ;^d)aao)^`{vF@&+mwW@e-X(xb^P z4MeWKStRpN<%;dk6%j&s&lCe9X);YPg>)qKr#kJXN#*$``_)90*x`*RJX1g<(}EH> z6bRqbj`!sKD_R+mB*8UTPta#*e85OJs4qj=f~+~Nr4EM!o86m_G-LuXQ%9+=i^>Oz zaf<7pV9kNju`IuOfPq@j zFpP?J5-)i}t|~-U9R)rdGUlyDJ!%=UBUnogMOcd10uCxwsemV?A>0kV@CIs*tKwAf zyk%pzS*O|`q+-06;k>Z7i$rk4v@8sF9#3la zq>ql(#aCG!k&244IXl}&f`2sz6!pNS#c{Q_4?GZ`HGUiEJ3O_4mRYPmUkIA}gl^9(b%fhZ)X# zRm+Qx4_d^$pBd*LTG2$wvYqwF8;*FbEdx)QI9~2mD$L6b?8SMo?a3<`>Q;(#2m%`N5N3&mjEzndvJq&CU!Z`xm#PZbJ%-NaR5anhzo zXI;Ih5c0zw)iO8BnuQk!n$fh=+T~e?-4w^MU1S|QQ^zbR*D<6+1E}JmP2GD?7bkTf z3{ooOb6PrQlwbF_sP!K!?Ez!P3B?u*nNzR;im&CPL|C3`&FmgY1Rk}V@mFASjJ8|)@~@)GG+Pi zQL;ehs*OO$l>l?=Rnit&ipi0RlIkeJ0*5CRlhD~*($j=jGKFEutqc1Fw~Q1Ojig8- zxJF^n*GYeS9mD|;WYt`k9Y{3Gkbohr$k)ul70y{(N*_B-bhc>0aNJV4R05jI0wPuJlyNR!DFSNTtM%Z&apncvr zQSNI8!fS1*=u`MY?#?^6BX|-XQBSA3n%?l`+9si>eW8@}=kIK;JSnV8 zt06n?oXc)c>ZZCWD9SD@&bp~iYnQL&W@x?*z3@klBZB5XGScEU5Zw-h{{VFR*QX?B z9<^UgyN|`XQD&4!I9!<9s<8ec?Z;YMDP2F&?slxn#zDwx^XpTRmD)RDIJIQS-Y|IL zsNGtJQmx3XM_lv()G{VVO2MN<&M#8ZKqZrk!*-h5LI)sMpj=yqQ<0kIFZA=~GX>(6 z>{@1JuC7(Kz6ESs>VV^rMRS)HWlsx=r{+y=c+EQ|7dyYV222Bra^8{G7_J?4Sr`0j zW!Cnt9GYq*wstaK0*9PcSHBIwHOWb?ah`t)fNNcO{OY+ybF7;00CF)@u5|@SnR9>z zbCPQthBt9krqs6+&VID{1mtT^s1i2=+OlW7CC?)swN39?hBNt9V);KY;;WR+w#g^B zBocaNrqZ+_tt^*U4p!PG+p&7N_w*G>fpx*iUffr8;A?np{5Zln5#;lj%BtBZ*aO^g zR|TmPm64p%$>s$L6<2@z%=#RF)}xj`QSv!-K%S0|UuB$}llEC^Zv%ec#uGgKr(4Qae=Sk(!G zZzB|hKtSV~nj^EdHB%>=ol2340A}pHYgpaO1WbqrBduh*?c^F%xMdtrhRYWYS=)Cc z(^6s+7{I6*%N&|B#ZZf{BCKGeb5$B=0u%QHxRV^BEnPayhHu$AVJ z(jrg2QV?@ajvd4S)~tP!PfC``NJ}k2?8s#M!=bIXJVx@NTZTCu4Rdg==~YhvoL5md zhwW2L^VUPW4E3(Ky$%UST@1WUaMDW_e6mUFP(lHn6yOZgp}vv`{Gg!q!KeW&+llX- ziW>xLURuW#YSEs1)iDzfn1%rRQ%tVv12G_U2DNTA2xYWb(mXah_M|RBNKLGM*^Oq@G)8T(3r+6S_SDq(L8I%HCtPMv1k|9BZgR5f;x^mk9yOF5ZTKGP%>Eb1Ju@4 zwfq|7k}}(~rsjs;B7cOBzTaNn*rgj?H`7tL$;G(;09QF}J|m41So5>V5?pcjSaG!b z;=AjsJ3kcKEN+>$Ve+v1(fU4_rx`=o~)(0Pf`D^3U_Fn6Li;;TN?l zGLqT(;vHGNsnonGXm`=&uR%2LC85Z8h+X(D_7fA5T>j~YMvQe+_*Zw~<+9fFl)wJ? z#?0f&J@ZuLwX*S@xk>Qx_2chEeXEq7`u_mJ8naq2%#25xnE7O0*!QlfHBuJpc@*i+ z6>Ro9Xypk!mc_DBunF!v8kxsZI#l-XSYPRp-iF5H+$0C)UvGR?O}3;O=1W07JfE#++}o>gMn!58qxAoM#uxmsk9>sJgrM>PzC zoKbRFT5*UEO3Ltmi(A7!Eo;e(HI>5{?ob@%3E_vj^{e`v-bI{`=%m*R3VDot&*5DD zb*wv?PhAhEwJW5NGK3IE1az));xi~G2k@+~1bBh&O6jg)DcsrF_YG)RtL9bP3OHu zj8&I*G3_K(V?c*42&TxzPSOaajrK?wjDbsaC!FA--~r7crZOif^JkG$eYxtQ~XZ2Ght*Zc^R#ZI@Sw%U+B-)mw;!7Y`P9ZSPYqp@=|Stbk*Rr;=MI zC)An%u^cYrb}HOSl;^EB!92uZ*~e^EsNxDp&OqsknFMzbtcaV+9lp4xt=fZvO76K*$No)!ToZR!m!g83weh$qkB;*x0oLaLshub25SH zis!AOIR?5dIUSDP!>v*gtrKr-ircY^&AE5G)Z9aWEPY_wLZ?sBQUEh32 zIuC9+sA|ifvD-y7o*rLo`u@EMwQB*PYWDi_shP4Py$jcA;5I)Wnr$Ks}nTNwc> zAMRuyb=SV{Z1MP3)sK;>X|hXomRS&qRl5vwYl-l+?A{c-)NHQ-dvCNe9Cby<-UGNj zaqV3YUeY(}ju}c=YN;owvtw%h7F@~?AQ_11CvHBSzLjTE(BknWly@<*`O)Q|kn|_e z{{SkvcP+NPV4~htmfeP8l{>eu*Xvn}HQ$95mUzsTz+{x^p2T_|ZuQL>N-{kvv5jh1 z+GiNLANkX*Ex> z+>2|l@yHGVVe+@Qtr04^ywuy&sG49vK(B%h~h z&63k$C(^oEFjSH=?^yTFp%2a5=~R&{z_?JL@mb6{C$(I;a?iodQ;_XLS`c&oB%c;8 zqd6oG*164+Mq-)95RJcuY50a?ArX%4npU)Htug1u5+wu=P&%5{I%s7jcHd)D!dipr zUL=bCMP#s(FwDcI8==RhK(ASm&`Fl)1~}>XSCQ$q^XmF&wS+3FDMxR|gI#kga2(4P6EM#_|$?;}y=`J{<8|^MDnNbN3`Wo;%YQLL_a(^HEA1 zp1BkSA&3T^Milc*kzk+8!0%JMlE}FNslb7z88yEY5q+PwaYc!gtj=2IHpwTgRgktZ ziq+ImD;!pGfLrFI(NT9RExSHVMC-SX^=+J^73eC%MHAa@$lNIuWTKqnrTe@Zb1!jG z9Ft5K{o@20gr2yk$&N8fSp<0FG!jEfl7vzS=9DV>)KXwK6bx>8%|9Zd+s1QCnk+QS z+NgnU{{V0RTyh7kR0lN0SA4JqvDDL-wQ6$`qV7J`r3IS#K4$(%&1ZlDf-6f-V9e_5 z0Kn_gnk9@x&pm4MX_F)ZQ;nybdQ)PxGfd(l0)xlBZrod6Er8z;RKNt&5t9r%a94nO z;<{$BuuUMyFl8%{LFj7+CBo;fDXr$Q!ZGKl6gCMEi?+AU-lw%mVX}MGd4M@Q)DqiB zSPi69_SWtQ10ed=QZ|c6L8x2V7~}GlLCrEap&NFAp7^Tpz{N)LP7QS$UxqF&)ovvX z6a(fgR~a9V^XczXX7on)T|-E+Yl&4>HfB8(S4Tak*riJwX=1q5r5Q_P&V4&k zr)suxJLz5=wr99cmP_a57uA6seZNfBhMlJAx-!U?mqzPWxX+n8pPST=r~d%1wdMAD z%W_kVXYA>G-(o1VacY7+M^ieL_f)r40u*oe6mySLnz5*P`Za=HT~GF{LQj@7-Hv~{ zzJL$uS7fn+L9jP6v6A7~1~}!3><^`2Yd#zC9p$@UNvXyqGTkg+%!K8@2q<5c|1t4&p)kwZjRulLj*-iP_tPJYkf*y&W(Azn-0 zM?`(ct*O;$>wJk2{EkMd`vMv{hVv31mQFZz?Vr%sJ5r}JyO~s@d1&9` z>*wZq+}{ z?r>36VN=VYq2dd9^tmI68!E(+C?1`Uy(~X#drv=U%8k-5QUU10+O~9iiw_jp$lx(V zf8IvJ4IB39*X!Q0wTs!bJGMn9?FgWxxcOT=k&mY&A8c2fDleMH(R+Gzsj7c%b^gDk zMd$gM1Lbc+O?50fW7?`uEGY%Fj^Z1Oqx-u|7GgWLdoTFYqq;l{f-%9(bIKg2E79yn zG7ve9i1hQTfpNhY_nm225!(N9!;x{Q^k5do8aq>FIcv1>x;~y?Ee6kWb#H&dfh8(jN;S0 zSo7%}Xi z9C}o-GbhZxl-OcvUmXW?SE7v~EU&cj-hxNj+{pv(^*QFc%a0I8bEgOuc_Rk6=(dST zA9pooNrrw}cN#{FGOCgoXVR@$VBd|y=6lI-`03FYuu9Ha(GK7Uxzf#TjQ{12FS2abo(PP9@wZ^L`%I+L4dLB6g z&<;&f_nnY9axQt9HeMMT9lL6}s71d4^JWO-02!+hB zmf(N1gCFOb)qm{mZ%%_zv7af8qz<7wui$e}@h666@fMevWszin`O%-^VUARKWPYGl zUxrSdrBC)LqKz%C{{VJGoSprT{{Uag2L-c@~-2L%h+L& zU!BX2hw$hB0M%GpCZl0B{N5MTE<;_zhP0K=4Dn+igWQhexWzgR9W>oQx?H)jo8>-e z%OTIoPdz>AuZ#2>G4@OI401m)nPeY#xFgf}vG`V0lq9)XrXo{?PIzolzI%b<2%}Ue z#kO0`pOu%ox2PlXIO|>2rlV^1`xJRDi?_)=exCKu_(J~9^3F&uCKoeC-cvhr6;CUZ z?l{l2L#%1?wZW1&+G|DVqy1z@*W6ZBIiT#0+6oxEDvOnrwXX#>r+D|edZLy)rndtC z_B#)3j>fW+#Pj%K*`kph;%3-wdKL95I}zz!rkkrKm#37}CCo@S8P8hE*0hUX8G(`m zKl+=9z%3XZ{l};^y$ZKhmd6G(YUVEVJKqPQyl!HOA@W?tcw_$8*QO3dUGVO^EXghPp&R!D zduTY=f4rm6o^#mnXsaGq&j+D+R_Z4SX(h9NkyPpz9tNG7XnttM!wEm@$Fco$T^^FQ zJ|}=a@3dRVhm&>#sXXmr)Hm0)SJo}8{Alq;7!*k{3jvc84!f7QJ;!>%)EaLCiEbs| zZ{&k1oeo989{uy^YhQM7zf*?wDOCBKM~JlzMZPCoyfd;vYTss4jHbmrmK}j^KML(F zyxW^Uw8a@^{v-!;AoU)r*PqW7=iULky4JOejM~Wa%;s6HmjNT(Ny}%yUI(RnP0pKS z+G_|xEUK6iK)4}s(0YG9wdd5Pt-0#buBRlbEqk28Tz3t)cBFaK`tCh5(w%jr-P$az zB#R=)9`Ps~xBDyyLyqUIWUb=20P|e#HZ_vvY<1G__F$!fp4ios$XvHOuQ5Y4)Civ_fdZ)0#_dV%F*~3&8$wJvbx$YuYX@ z#;c)6tyoXxL2RCKA?uJxZ2RLNSD0veKlYXV#knB}wvqQA;VXvE>0XQAIqk1BiQ#k~ zV6bL+WH`snob~P2x}h7w=R21rM7x$eVWFkJg)Jq5HIm_8FrhmrNReC z46HIe$E`~}yMX$Qua_hYqwPq&84vul1Jru^RXe>tHQvh;2>=FYB0Mtx0MAIh53W5b znoP-aSdM6Ro+`U5m2a1dv@Py}MAr9dcMA-R;4=?T_lKn}2hmB#BLp8pD?2jGheNyG zlw0pyn!j-XLID_WN`+f$l>^?U#gx_{7=uk^0kA65C?K43L>B&ZxamO1-gH}VpnTPv zF)h}vUBY6xWCu9uRU0|Mpe|Bz%{lF)NW+oAsUAM`=D2Ah^3DJh$z>p{bGDaah{^Aa z((Wmobg9-zzRJMkIP|7Tn#VN5z|A8ZQ*ub41SXrBc0~%x2Ov@rb>c)&RMMv;3S$5c z0prq@0VfzWp@FiuR_SD_0N;#b6;Qc$1+Z#sTQMYMorw0OipEK0Uh(WbP3TgIk7XRqC`c8WM&K%p=Dh@Y)&8yhCr3I#}Xz@;+HOzWeffy1o+LAK$KB_t#dYawSyfJU1v+5Uj zEp>X_`Lc7*p#K0K)cZX@L(`Gq(qoQWu(^=H%FKP(jN|G0RxYb=tHXO7+2jnwfr_FI z{{SiKea%$m7^_1;;b~NS=lY+$c`&A;ZF9>IL>(Gi9I@0z8~>rwy&o* zl@Z?wF-i%*$54KTpQ>nYsrYv47GS1Gw}u}x79TLj;y+sDybA@6kE!h-lXx~XlQ;1W zSC7)1Ay3&iyE3m{qpG8{dwH5Sb85fZj+Vg36mPg^c(u7^ zI7@4+xNr8l5&84@*MFwXZE>W*I~2pl1MfY@3=iTBZ(TXAV~YG%3qohga8103<^b{n`j z_3K=|rEvt_ZijaACA@$bCY0nM>Ys*q{3{#4c2^p9rtnlVry8CT^J0mxVN!S7qTb(89L0j^ac zk&|u2C*~^O#mA<4cBI@YB_tIiT9sD{od~=~W}397I2lZM+ydiljz``MJN^V3hey+= z@WEzt%YnTZ{EO^A#-Y=siCpo$&P0Z+&Y@EMic}JPb0?I6VmK*wqh-ekr%Q@N3(W0cnsj5F2s!K9$(Z zrL~r=Cx|r24#~o;)DessP=6Ay_s36gYtOzV>AHRIhA#ChJA`1e2?|Aw=Q!ni5$RP@ zHkIOY(5o7-e${){(fw&mjy{8f&3a6dBwkpGHaXk(*bq;r z9li11yqm&WUZbYkLv?5zjDM_vZ#?>_>MN#$T)EZn;jq3hWek9<(nT3USKW^)54Cf( zsiMNUE^2P&tsBO$Tr~E#7Oy3wk1S&DBl3uWDhq?(ulQFk=VO&g5>>z}BJ^;1$gba9 z@a}*sl~CRWd-vD}?PbZx_joe5CH<>0Eu%`L}FOt#*2| z6-Mjyu5B$#$XJdEszA&B4zO#V5F14fP0hR8uu;ejjmPp8vv+9+hCDMpyp6s&EsW7ZyD6JJ}jur$Tq7~81H?tS{#Q7)&Y zJSl&t+o~!y+2ZN)ny29nPRhdFtZp_&(7B0nbI2TmJ@RU_Ph`F))U1%9hTUZG_TtL*#b9-|N!FnQUa7tk%jL z%Mg+^C*_yado@to8|>I5Mq!9A0f8LAVTOH9C~(oDV^e8ueHotwLRof`jx$y69iW`7XCC#VJ*<$Sbu1-8 zhXrs~>N}5mpw`VPEiUC_(;~8IqiggizV>p*l6uuyG8>xf^s7rVX3Z&G`0rfiq_K;K z5#X&WSV@_zNvIrSrCB2atI5`ZkdyM%EZ7*$JYG5a&IP*>Db3h1qqLqj> z8EjO6eLnSc9vy}WZwH^#-l|{eV#p&amJRvUts_g-H3`IeWt7)4j-nxuqulfrt;|xn zq2d^!hU5!!2LPJ0;j25%YVmC>pf1aUE0csp^<&%V*1JtJ;k}~UCD(~=THCe& z_AS-F3<~e`O&dwEu=1j|ODk0yysx-O^<&ijberv|qJ_1dh{w`A8)_XMNT9fuGBWHk zihiB{09w%3uAfG=X!Ow?&GQggGh=W2P5l;~f2qkf%Y7Ia;~;@D#kii|ApWMXk}X3^ zyaMJp5u<(0=Vn%Q?s(%T@vUh?TfexPvWt%_9lXbBacVbmoBse3d5jq1D5YO5-`tV+ z6VukadwJ~cwBU+}B4sFJLBbA(n{}tfduk^P#D()BBdEyfSymU8RyTfAN{S}nsce#K z8kJ```5ja+)Kv#_v6Aa80UM$8iIV+eC<@^~mLG zp7GA2(P^LBR&c5`EF+biXXHH&2fy>EybZ5k>Y8l!zc%}K%K2%5wY?A5KbWqG)Ap44 zyBu`!3YA`1qodO0xd1@yHlAZY%vUMmUke*;N=w9Wmh!gc^hWf~eR@{Dr={LUezyYX z!jfV3gOVHD`PR?&WtOd_`ByvThGp9#?!eD-G0^6@=}A*(b|;yoRvmvawo7lQXz{(o zh~`AuE?fb)a7WiarDGv6uq=(2h4s6O6Ei)an2D2qtSH3%3r&h(daY9QrzmA9n3Z$M{4Uozi-i_{L)TGB~;Nunb;5qX1as`0VM^sK!;#q`_V(@DtrCIEF@^~wJLJ!^_w z(V6N}geuT*lhtaDQ^lWX)AgyLgovS-`OuX2{{ZWQ(z~4$w}>|1fOZstw)$}CXJuvQaUz2LtTyzr7QM4 z->k-!MHr(~PSfJk^=Q1!&e6F-&F{hg02qY0acwfgeQXSo6zaeZP>$e?{#DNS zo9*{y9wOyr`D94f9OHsdZYy6y)2=S8QcIZ@ITz({ypnqpjyiR&dT~&dj@)GDUYu)R zUZ*b>r)lE5ad8d8t>i0-;gdUxXNO$%89e$|L8j@3SP`I{E~jQQ1lZw|rq=ZxjdGf& zhpl`+;)l4gSKS*4q29!B+?C^vxyZ$K6WBv>X9lOE+?eH&1FS76WN=5!o}l^=ahlSt z7{W@V9_Dm$)2)a4wNof6VGbtu|MNA7}-Ws7eF zKlicg)YZx{lrIg=I7+QK^0&YBW4w;xjN2RJE`;rFOk^H%dU27**0`?{=$b!?JU4Z% zG|3CWZqf^+TN3P}w@!T}2(F>UT?SpEGBuIs2saNj-Y4Wxl?J_?WayY&AaMnco5pnp%}b`6Gs!qaJVJ0oPYJ| z-+^@VKA}3@!#ss;@}b?EYZ5whR>yJ(ODvV5{f+z>spgYEB9#RcDm zw5Qf|sm<948d&8c4-q*yJwY5+=|8(IF?9?Tt%JetO+aXJ#ku^kBRinSHV+T zLRicdM%G+ls}EE4u6GA!j;gqYI&hO(-J_JZLA#OtYeWsPbnRL8q!FJ^ykixhguVde zS1CJ~nuISi;0m{-XfLQ-!D@$Tkesm@+6N@!t-Y|%bM&p<4P~=)a~kv>SYo*c)~*ZQ zK;q>c4r9lD7?$Tu{{TqbuK`p0o?>JqAvyIKQ|LfB8LuYLt`|+zWlhJ)b}^rEpZ>Lb zuZgwEZtOMiv2L`}?RL3Sg_2fQ$j`R}>x%G^rq2eRH3*P7g~K>G>5NxY-P$pP6k_Dg zq4Y;De|GHg%N(Q3LC_2htPkZ^=hFO}ojX{C+W>7lxEzmP&Y7gid#ve~H=#e)FPF7? z9fJdpTvuJ;Ei3I>S+mr}Vkx*I_Z@NUMO-DzD;Y|cwWG|Q#lfcN+P0dvwyl}eXC(Z= zhw!y?{wBGzzS8vT*p(LQ8>Bq#8s4nTXPPjXE>6^ z`A3?koE&%bs4Z=!woe*K6I7{Wnsc<`Esmrgcjn z-OU*pGe&qdDYeq&gxWI|j^Wv@cb}LybmyKbO;7Fiu{Gt|rH-i#M8!G`i9(J+9l@+P zBz;lkmr#vXTeMlNB|(j@2Wn6A73{3mtFLnhShJr_yJ*{RO#H3b`yYDAY@FwNPOlVHv2RW-ICGyE9<{tG;<#lj#$mvXxXMv$3oP*x0nwm-8 zQ}bhrgwP>7Q*bHAr5LFh?4A|y1;zVAsd$<;Hh`Zl$~NXh{?h}Fzrr$oNaf7;rQD&-rFO^U&8nuUegcB5mYYY-0W45`tEK#O>-p=K%5E zxnGG|#rO95Xi?lWRxKK{tbZ#>xQvEwq#< zu8+hk=U7K0vl6m^IrSXTiF~qlG|M9^s9$}}XRy+*EM6n!OAP2&F4-TK)C%H!KdM{k zz9JUVzSL(n@+cfB?fQXRS_2l-WCxZa|PV~XxE$Qk*RV1wJzy59`F zy~l^(^IOc40pv-&y*g*`te=UO)7p4)Mue)ocM$}9ayJ|r;yej=aXzbjvri;|z^{jF zjEDaK>(_dwr$+Cg;!6`a)v?;#Y8DzT@0aJNnH%L6G|4O5r>`}kuU*HgXwoIDJEjZf z+cTaw^&gny@vck6kt=GJz+GXK4<5lu4tuBS{{Zey0l?8 zx#vCz@g}2ntV?P@RJlX7V?%);Il(@?N8w#Ji1dv{`Z(^sZVuT4z5xfJ{*{TTpRjng zJR#zgxsNP2dC-S^4TtY82jVgG^sP+`P`Z~_MQKP^Ly$Ujtlx-A7A-nq z{;-)4pKJqJVQWdgUgvf@0~J#DJNa(x^$i%cqpQU%d!yJo{{WMbT=tc&q&kO_uq1Mg zpE2Y&Z2mP@;kj$A8dzm!DGkX80qj60^{YCatQMM$&yx~^zjjFHHHBo|V>$vcKm7RI1-02=W`#rNMx72*LxQ0dC zg^12T?0BsmcG5d*_YgD2jT*_d4hG|rPw89YFJyDzAsVxl`kiE!mjNaO+4~l5)*gGk zyQz*M2sjwPQawkn<5)U8Q>EMKm$A7Lvu#%Ck--Fej2!;}jcH2?+}Qy621tP{rvx3j z$UdO+SbA2cb9-xRsmxT}6;~_Ac&%@UlbL)ua;%69 zO0uH`}eNR5!^%{k+4zdk zTf`$1Dc%>M;=J}w0q zsxBgR+T*VyC-oH6d8E_oL#fFK&2-Sxmff`dZY@&M%*c|iFYV9_$LewV(rPj3*D%d3 zoX|al_#0n6H2MRdl1rKU=xwg>sWeB5&r;c_=`^}4XG-8 zv(rB{H>%fkOj?ZWmXbID z*B_;EQgy6bH=1#mD9ISJe7#LUDmXb|PX`VSER0*NA_$?2%#bNK6#J+W830p}j+Gmd T21P_!k5fzmuxO^z)PeumQww23 diff --git a/app/src/main/res/layout/view_search_contact.xml b/app/src/main/res/layout/view_search_contact.xml index 167aff6..77532aa 100644 --- a/app/src/main/res/layout/view_search_contact.xml +++ b/app/src/main/res/layout/view_search_contact.xml @@ -31,6 +31,7 @@ android:layout_width="@dimen/view_search_contact_info_avatar_width" android:layout_height="@dimen/view_search_contact_info_avatar_height" android:src="@drawable/default_avatar" + android:padding="@dimen/view_search_contact_info_avatar_padding" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml index 7bde084..95e49ff 100644 --- a/app/src/main/res/values/dimen.xml +++ b/app/src/main/res/values/dimen.xml @@ -162,13 +162,14 @@ 5dp - 15dp + 5dp 10dp 8dp 10dp 5dp 100dp 100dp + 10dp 2dp 100dp @dimen/font_normal