From 3f11fb3937b8c653b44fd2fe24869d17037a3430 Mon Sep 17 00:00:00 2001 From: Chumva Date: Thu, 25 Oct 2018 12:52:32 +0300 Subject: [PATCH] Add ability to add new sharing devices and save them in shared pref --- .../layout/bottom_sheet_add_new_device.xml | 114 +++++++++++++ .../res/layout/fragement_settings_dialog.xml | 36 +++- OsmAnd-telegram/res/values/strings.xml | 9 + .../net/osmand/telegram/TelegramSettings.kt | 64 ++++++-- .../telegram/helpers/ShareLocationHelper.kt | 5 +- .../telegram/ui/AddNewDeviceBottomSheet.kt | 155 ++++++++++++++++++ .../telegram/ui/SettingsDialogFragment.kt | 99 ++++++++++- .../telegram/ui/SharingStatusBottomSheet.kt | 2 +- .../telegram/utils/AndroidNetworkUtils.kt | 149 ++++++++++++----- .../net/osmand/telegram/utils/AndroidUtils.kt | 45 +++++ .../osmand/telegram/utils/OsmandApiUtils.kt | 77 +++++++-- 11 files changed, 675 insertions(+), 80 deletions(-) create mode 100644 OsmAnd-telegram/res/layout/bottom_sheet_add_new_device.xml create mode 100644 OsmAnd-telegram/src/net/osmand/telegram/ui/AddNewDeviceBottomSheet.kt diff --git a/OsmAnd-telegram/res/layout/bottom_sheet_add_new_device.xml b/OsmAnd-telegram/res/layout/bottom_sheet_add_new_device.xml new file mode 100644 index 0000000000..d80986ee98 --- /dev/null +++ b/OsmAnd-telegram/res/layout/bottom_sheet_add_new_device.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OsmAnd-telegram/res/layout/fragement_settings_dialog.xml b/OsmAnd-telegram/res/layout/fragement_settings_dialog.xml index b196c0372b..5d2bacf752 100644 --- a/OsmAnd-telegram/res/layout/fragement_settings_dialog.xml +++ b/OsmAnd-telegram/res/layout/fragement_settings_dialog.xml @@ -95,11 +95,13 @@ app:typeface="@string/font_roboto_medium" /> + + + + + + + + diff --git a/OsmAnd-telegram/res/values/strings.xml b/OsmAnd-telegram/res/values/strings.xml index 8b3555f5f1..80a767089d 100644 --- a/OsmAnd-telegram/res/values/strings.xml +++ b/OsmAnd-telegram/res/values/strings.xml @@ -1,4 +1,13 @@ + Error adding new device + Enter a name for your new device. Max length 200 symbols. + Device name is too long + Device name cannot be empty + Device name + Hide + You can create and view the Device ID in the telegram client using the %1$s chat bot. %2$s + If you want to connect multiple devices to one telegram account, you need to use different Device to share your location. + Last updated location: Last updated location: Successfully sent and updated Not possible to send to Telegram chats: diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt index 451918a91f..0b4138ac04 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt @@ -11,6 +11,7 @@ import net.osmand.data.LatLon import net.osmand.telegram.helpers.OsmandAidlHelper import net.osmand.telegram.helpers.TelegramHelper import net.osmand.telegram.utils.AndroidUtils +import net.osmand.telegram.utils.OsmandApiUtils import net.osmand.telegram.utils.OsmandFormatter import net.osmand.telegram.utils.OsmandFormatter.MetricsConstants import net.osmand.telegram.utils.OsmandFormatter.SpeedConstants @@ -23,6 +24,8 @@ import java.util.concurrent.ConcurrentLinkedQueue val ADDITIONAL_ACTIVE_TIME_VALUES_SEC = listOf(15 * 60L, 30 * 60L, 60 * 60L, 180 * 60L) +const val SHARE_DEVICES_KEY = "devices" + private val SEND_MY_LOC_VALUES_SEC = listOf(1L, 2L, 3L, 5L, 10L, 15L, 30L, 60L, 90L, 2 * 60L, 3 * 60L, 5 * 60L) private val STALE_LOC_VALUES_SEC = @@ -76,10 +79,10 @@ class TelegramSettings(private val app: TelegramApplication) { private var shareChatsInfo = ConcurrentHashMap() private var hiddenOnMapChats: Set = emptySet() + private var shareDevices: Set = emptySet() var sharingStatusChanges = ConcurrentLinkedQueue() - var shareDevicesIds = mutableMapOf() var currentSharingMode = "" var metricsConstants = MetricsConstants.KILOMETERS_AND_METERS @@ -151,16 +154,15 @@ class TelegramSettings(private val app: TelegramApplication) { } fun updateShareDevicesIds(list: List) { - shareDevicesIds.clear() - list.forEach { - shareDevicesIds[it.externalId] = it.deviceName - } + shareDevices = list.toHashSet() } fun getChatLivePeriod(chatId: Long) = shareChatsInfo[chatId]?.livePeriod fun getChatsShareInfo() = shareChatsInfo + fun getShareDevices() = shareDevices + fun getLastSuccessfulSendTime() = shareChatsInfo.values.maxBy { it.lastSuccessfulSendTimeMs }?.lastSuccessfulSendTimeMs ?: -1 fun stopSharingLocationToChats() { @@ -216,11 +218,15 @@ class TelegramSettings(private val app: TelegramApplication) { statusChangeTime = newSharingStatus.statusChangeTime locationTime = newSharingStatus.locationTime chatsTitles = newSharingStatus.chatsTitles + title = newSharingStatus.title if (statusType == SharingStatusType.INITIALIZING - && newSharingStatus.statusType == SharingStatusType.INITIALIZING - && !lastSharingStatus.description.contains(newSharingStatus.description)) { - lastSharingStatus.description = "${lastSharingStatus.description}, ${newSharingStatus.description}" + && newSharingStatus.statusType == SharingStatusType.INITIALIZING) { + if (!description.contains(newSharingStatus.description)) { + description = "$description, ${newSharingStatus.description}" + } + } else { + description = newSharingStatus.description } } } @@ -278,7 +284,7 @@ class TelegramSettings(private val app: TelegramApplication) { locationTime = getLastSuccessfulSendTime() title = app.getString(R.string.successfully_sent_and_updated) description = app.getString(R.string.last_updated_location) - statusType = SharingStatusType.SUCCESSFULLY_SENT + statusType = SharingStatusType.SENDING } } } else { @@ -350,6 +356,25 @@ class TelegramSettings(private val app: TelegramApplication) { e.printStackTrace() } + try { + val jsonObject = JSONObject() + val jArray = JSONArray() + shareDevices.forEach { device -> + val obj = JSONObject() + obj.put(DeviceBot.DEVICE_ID, device.id) + obj.put(DeviceBot.USER_ID, device.userId) + obj.put(DeviceBot.CHAT_ID, device.chatId) + obj.put(DeviceBot.DEVICE_NAME, device.deviceName) + obj.put(DeviceBot.EXTERNAL_ID, device.externalId) + obj.put(DeviceBot.DATA, JSONObject(device.data)) + jArray.put(obj) + } + jsonObject.put(SHARE_DEVICES_KEY, jArray) + edit.putString(SHARE_DEVICES_KEY, jsonObject.toString()) + } catch (e: JSONException) { + e.printStackTrace() + } + edit.apply() } @@ -376,6 +401,8 @@ class TelegramSettings(private val app: TelegramApplication) { e.printStackTrace() } + parseShareDevices(prefs.getString(SHARE_DEVICES_KEY, "")) + val sendMyLocDef = SEND_MY_LOC_VALUES_SEC[SEND_MY_LOC_DEFAULT_INDEX] sendMyLocInterval = prefs.getLong(SEND_MY_LOC_INTERVAL_KEY, sendMyLocDef) val staleLocDef = STALE_LOC_VALUES_SEC[STALE_LOC_DEFAULT_INDEX] @@ -410,6 +437,10 @@ class TelegramSettings(private val app: TelegramApplication) { } } + private fun parseShareDevices(json: String) { + shareDevices = OsmandApiUtils.parseJsonContents(json).toHashSet() + } + private fun getLiveNowChats() = app.telegramHelper.getMessagesByChatIds(locHistoryTime).keys private fun updatePrefs() { @@ -567,11 +598,6 @@ class TelegramSettings(private val app: TelegramApplication) { R.color.sharing_status_icon_error, true ), - SUCCESSFULLY_SENT( - R.drawable.ic_action_share_location, - R.color.sharing_status_icon_success, - false - ), SENDING( R.drawable.ic_action_share_location, R.color.sharing_status_icon_success, @@ -601,6 +627,16 @@ class TelegramSettings(private val app: TelegramApplication) { var deviceName: String = "" var externalId: String = "" var data: String = "" + + companion object { + + internal const val DEVICE_ID = "id" + internal const val USER_ID = "userId" + internal const val CHAT_ID = "chatId" + internal const val DEVICE_NAME = "deviceName" + internal const val EXTERNAL_ID = "externalId" + internal const val DATA = "data" + } } class SharingStatus { diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShareLocationHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShareLocationHelper.kt index 74001a6d03..7994dbdd70 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShareLocationHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShareLocationHelper.kt @@ -5,6 +5,7 @@ import net.osmand.PlatformUtil import net.osmand.telegram.TelegramApplication import net.osmand.telegram.notifications.TelegramNotification.NotificationType import net.osmand.telegram.utils.AndroidNetworkUtils +import net.osmand.telegram.utils.BASE_URL private const val USER_SET_LIVE_PERIOD_DELAY_MS = 5000 // 5 sec @@ -51,8 +52,8 @@ class ShareLocationHelper(private val app: TelegramApplication) { if (user != null && sharingMode == user.id.toString()) { app.telegramHelper.sendLiveLocationMessage(chatsShareInfo, location.latitude, location.longitude) } else if (sharingMode.isNotEmpty()) { - val url = "https://live.osmand.net/device/$sharingMode/send?lat=${location.latitude}&lon=${location.longitude}" - AndroidNetworkUtils.sendRequestAsync(url, null) + val url = "$BASE_URL/device/$sharingMode/send?lat=${location.latitude}&lon=${location.longitude}" + AndroidNetworkUtils.sendRequestAsync(app, url, null, "Send Location", false, false, null) } } lastLocationMessageSentTime = System.currentTimeMillis() diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/AddNewDeviceBottomSheet.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/AddNewDeviceBottomSheet.kt new file mode 100644 index 0000000000..e5f4317d0a --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/AddNewDeviceBottomSheet.kt @@ -0,0 +1,155 @@ +package net.osmand.telegram.ui + +import android.app.Dialog +import android.content.Intent +import android.os.Bundle +import android.support.design.widget.BottomSheetBehavior +import android.support.v4.app.Fragment +import android.support.v4.app.FragmentManager +import android.text.Editable +import android.text.TextWatcher +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import android.widget.EditText +import android.widget.TextView +import net.osmand.telegram.R +import net.osmand.telegram.TelegramSettings +import net.osmand.telegram.ui.views.BottomSheetDialog +import net.osmand.telegram.utils.AndroidNetworkUtils +import net.osmand.telegram.utils.OsmandApiUtils +import org.json.JSONException +import org.json.JSONObject + +class AddNewDeviceBottomSheet : BaseDialogFragment() { + + private lateinit var editText: EditText + private lateinit var errorTextDescription: TextView + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val dialog = BottomSheetDialog(context!!) + val window = dialog.window + if (window != null) { + dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) + } + return dialog + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + val mainView = inflater.inflate(R.layout.bottom_sheet_add_new_device, container, false) + + mainView.findViewById(R.id.scroll_view_container).setOnClickListener { dismiss() } + + BottomSheetBehavior.from(mainView.findViewById(R.id.scroll_view)) + .setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() { + override fun onStateChanged(bottomSheet: View, newState: Int) { + if (newState == BottomSheetBehavior.STATE_HIDDEN) { + dismiss() + } + } + + override fun onSlide(bottomSheet: View, slideOffset: Float) {} + }) + + editText = mainView.findViewById(R.id.edit_text).apply { + addTextChangedListener(object : + TextWatcher { + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {} + + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} + + override fun afterTextChanged(s: Editable) { + when { + text.isEmpty() -> { + errorTextDescription.visibility = View.VISIBLE + errorTextDescription.text = getString(R.string.device_name_cannot_be_empty) + } + text.length > 200 -> { + errorTextDescription.visibility = View.VISIBLE + errorTextDescription.text = getString(R.string.device_name_is_too_long) + } + else -> errorTextDescription.visibility = View.INVISIBLE + } + } + }) + } + + errorTextDescription = mainView.findViewById(R.id.error_text_descr) + + mainView.findViewById(R.id.secondary_btn).apply { + setText(R.string.shared_string_cancel) + setOnClickListener { dismiss() } + } + + mainView.findViewById(R.id.primary_btn).apply { + setText(R.string.shared_string_save) + setOnClickListener { + val deviceName = editText.text.toString() + if (deviceName.isNotEmpty() && deviceName.length < 200) { + val user = app.telegramHelper.getCurrentUser() + if (user != null) { + OsmandApiUtils.createNewDevice(app, user, app.telegramHelper.isBot(user.id), deviceName, 0, + object : AndroidNetworkUtils.OnRequestResultListener { + override fun onResult(result: String?) { + val deviceBot = getDeviceFromJson(result) + if (deviceBot != null) { + targetFragment?.also { target -> + val intent = Intent().putExtra(DEVICE_NAME, deviceBot.deviceName).putExtra(DEVICE_EXTERNAL_ID, deviceBot.externalId) + target.onActivityResult(targetRequestCode, NEW_DEVICE_REQUEST_CODE, intent) + } + dismiss() + } else { + errorTextDescription.visibility = View.VISIBLE + errorTextDescription.text = getString(R.string.error_adding_new_device) + } + } + }) + } + } + } + } + return mainView + } + + private fun getDeviceFromJson(json: String?): TelegramSettings.DeviceBot? { + var device: TelegramSettings.DeviceBot? = null + if (json != null) { + device = try { + val jsonResult = JSONObject(json) + val status = jsonResult.getString("status") + if (status == "OK") { + OsmandApiUtils.parseDeviceBot(jsonResult.getJSONObject("device")) + } else { + null + } + } catch (e: JSONException) { + null + } + } + return device + } + + companion object { + const val NEW_DEVICE_REQUEST_CODE = 5 + const val DEVICE_NAME = "DEVICE_NAME" + const val DEVICE_EXTERNAL_ID = "DEVICE_EXTERNAL_ID" + + private const val TAG = "AddNewDeviceBottomSheet" + fun showInstance(fm: FragmentManager, target: Fragment): Boolean { + return try { + AddNewDeviceBottomSheet().apply { + setTargetFragment(target, NEW_DEVICE_REQUEST_CODE) + show(fm, TAG) + } + true + } catch (e: RuntimeException) { + false + } + } + } +} \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/SettingsDialogFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/SettingsDialogFragment.kt index 1d749d87fb..cc3f7e721e 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/ui/SettingsDialogFragment.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/SettingsDialogFragment.kt @@ -1,11 +1,14 @@ package net.osmand.telegram.ui import android.content.Intent +import android.graphics.drawable.Drawable import android.os.Build import android.os.Bundle import android.support.v4.app.FragmentManager import android.support.v7.widget.ListPopupWindow import android.support.v7.widget.Toolbar +import android.text.SpannableStringBuilder +import android.text.style.ForegroundColorSpan import android.view.Gravity import android.view.LayoutInflater import android.view.View @@ -14,6 +17,7 @@ import android.widget.* import net.osmand.telegram.R import net.osmand.telegram.TelegramSettings import net.osmand.telegram.TelegramSettings.DurationPref +import net.osmand.telegram.helpers.TelegramHelper.Companion.OSMAND_BOT_USERNAME import net.osmand.telegram.helpers.TelegramUiHelper import net.osmand.telegram.utils.AndroidUtils import org.drinkless.td.libcore.telegram.TdApi @@ -21,6 +25,10 @@ import org.drinkless.td.libcore.telegram.TdApi class SettingsDialogFragment : BaseDialogFragment() { private val uiUtils get() = app.uiUtils + private lateinit var shareAsContainer: ViewGroup + private lateinit var shareAsDescription: TextView + + private var shareAsDescriptionHidden = true override fun onCreateView( inflater: LayoutInflater, @@ -65,6 +73,36 @@ class SettingsDialogFragment : BaseDialogFragment() { } } + shareAsDescription = mainView.findViewById(R.id.share_as_description).apply { + text = getText(R.string.share_location_as_description) + setOnClickListener { + updateShareAsDescription() + } + } + + shareAsContainer = mainView.findViewById(R.id.share_as_container) + val user = telegramHelper.getCurrentUser() + if (user != null) { + addItemToContainer(inflater, shareAsContainer, user.id.toString(), TelegramUiHelper.getUserName(user)) + } + settings.getShareDevices().forEach { + addItemToContainer(inflater, shareAsContainer, it.externalId, it.deviceName) + } + + mainView.findViewById(R.id.add_new_device_title) + .setTextColor(AndroidUtils.createPressedColorStateList(app, true, R.color.ctrl_active_light, R.color.ctrl_light)) + + mainView.findViewById(R.id.add_new_device_icon) + .setImageDrawable(getAddNewDeviceIcon()) + + mainView.findViewById(R.id.add_new_device_btn).apply { + setOnClickListener { + fragmentManager?.also { fm -> + AddNewDeviceBottomSheet.showInstance(fm, this@SettingsDialogFragment) + } + } + } + container = mainView.findViewById(R.id.osmand_connect_container) for (appConn in TelegramSettings.AppConnect.values()) { val pack = appConn.appPackage @@ -106,15 +144,6 @@ class SettingsDialogFragment : BaseDialogFragment() { } updateSelectedAppConn() - container = mainView.findViewById(R.id.share_as_container) - val user = telegramHelper.getCurrentUser() - if (user != null) { - addItemToContainer(inflater, container, user.id.toString(), TelegramUiHelper.getUserName(user)) - } - settings.shareDevicesIds.forEach { - addItemToContainer(inflater, container, it.key, it.value) - } - if (user != null) { TelegramUiHelper.setupPhoto( app, @@ -151,6 +180,18 @@ class SettingsDialogFragment : BaseDialogFragment() { logoutTelegram() dismiss() } + AddNewDeviceBottomSheet.NEW_DEVICE_REQUEST_CODE -> { + val user = app.telegramHelper.getCurrentUser() + if (user != null && data != null) { + val deviceName = data.getStringExtra(AddNewDeviceBottomSheet.DEVICE_NAME) + val deviceExternalId = data.getStringExtra(AddNewDeviceBottomSheet.DEVICE_EXTERNAL_ID) + + val inflater = activity?.layoutInflater + if (inflater != null && deviceName != null && deviceExternalId != null) { + addItemToContainer(inflater, shareAsContainer, deviceExternalId, deviceName) + } + } + } } } @@ -235,6 +276,46 @@ class SettingsDialogFragment : BaseDialogFragment() { } } + private fun updateShareAsDescription() { + if (shareAsDescriptionHidden) { + shareAsDescription.text = getFullShareAsDescriptionText() + } else { + shareAsDescription.text = getText(R.string.share_location_as_description) + } + shareAsDescriptionHidden = !shareAsDescriptionHidden + } + + private fun getFullShareAsDescriptionText(): CharSequence { + val textHide = "${getString(R.string.shared_string_hide)}." + val spannableString = SpannableStringBuilder(getText(R.string.share_location_as_description)) + val newSpannable = SpannableStringBuilder(getString(R.string.share_location_as_description_second_line, OSMAND_BOT_USERNAME, textHide)) + + spannableString.append("\n\n") + + var startIndex = newSpannable.indexOf(OSMAND_BOT_USERNAME) + var endIndex = startIndex + OSMAND_BOT_USERNAME.length + newSpannable.setSpan(ForegroundColorSpan(app.uiUtils.getActiveColor()), startIndex, endIndex, 0) + + startIndex = newSpannable.indexOf(textHide) + endIndex = startIndex + textHide.length + newSpannable.setSpan(ForegroundColorSpan(app.uiUtils.getActiveColor()), startIndex, endIndex, 0) + + spannableString.append(newSpannable) + + return spannableString + } + + private fun getAddNewDeviceIcon(): Drawable? { + val normal = app.uiUtils.getActiveIcon(R.drawable.ic_action_add) + if (Build.VERSION.SDK_INT >= 21) { + val active = app.uiUtils.getIcon(R.drawable.ic_action_add, R.color.ctrl_light) + if (normal != null && active != null) { + return AndroidUtils.createPressedStateListDrawable(normal, active) + } + } + return normal + } + private fun logoutTelegram() { val act = activity ?: return (act as MainActivity).logoutTelegram() diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/SharingStatusBottomSheet.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/SharingStatusBottomSheet.kt index 0708a2ff06..279c90b370 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/ui/SharingStatusBottomSheet.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/SharingStatusBottomSheet.kt @@ -56,7 +56,7 @@ class SharingStatusBottomSheet : DialogFragment() { findViewById(R.id.last_location_line).text = sharingStatus.description if (sharingStatusType != TelegramSettings.SharingStatusType.INITIALIZING - && sharingStatusType != TelegramSettings.SharingStatusType.SENDING) { + && (sharingStatusType == TelegramSettings.SharingStatusType.SENDING && time != -1L)) { val descriptionTime = when { time > 0 -> OsmandFormatter.getFormattedTime(time, false) sharingStatusType == TelegramSettings.SharingStatusType.NO_GPS -> getString( diff --git a/OsmAnd-telegram/src/net/osmand/telegram/utils/AndroidNetworkUtils.kt b/OsmAnd-telegram/src/net/osmand/telegram/utils/AndroidNetworkUtils.kt index 928ccc4905..3b5f99a974 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/utils/AndroidNetworkUtils.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/utils/AndroidNetworkUtils.kt @@ -2,8 +2,14 @@ package net.osmand.telegram.utils import android.os.AsyncTask import net.osmand.PlatformUtil -import java.io.* -import java.net.* +import net.osmand.telegram.TelegramApplication +import java.io.BufferedOutputStream +import java.io.BufferedReader +import java.io.IOException +import java.io.InputStreamReader +import java.net.HttpURLConnection +import java.net.MalformedURLException +import java.net.URL object AndroidNetworkUtils { @@ -11,21 +17,34 @@ object AndroidNetworkUtils { private val log = PlatformUtil.getLog(AndroidNetworkUtils::class.java) interface OnRequestResultListener { - fun onResult(result: String) + fun onResult(result: String?) } - fun sendRequestAsync(urlText: String, listener: OnRequestResultListener?) { - SendRequestTask(urlText, listener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) + fun sendRequestAsync( + app: TelegramApplication, + urlText: String, + json: String?, + userOperation: String, + toastAllowed: Boolean, + post: Boolean, + listener: OnRequestResultListener? + ) { + SendRequestTask(app, urlText, json, userOperation, toastAllowed, post, listener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) } private class SendRequestTask( - private val urlText: String, + private val app: TelegramApplication, + private val url: String, + private val json: String?, + private val userOperation: String, + private val toastAllowed: Boolean, + private val post: Boolean, private val listener: OnRequestResultListener? ) : AsyncTask() { override fun doInBackground(vararg params: Void): String? { return try { - sendRequest(urlText) + sendRequest(app, url, json, userOperation, toastAllowed, post) } catch (e: Exception) { log.error(e.message, e) null @@ -33,47 +52,99 @@ object AndroidNetworkUtils { } override fun onPostExecute(response: String?) { - if (response != null) { - listener?.onResult(response) - } + listener?.onResult(response) } } - fun sendRequest(urlText: String): String? { + fun sendRequest( + app: TelegramApplication, + url: String, + jsonBody: String?, + userOperation: String, + toastAllowed: Boolean, + post: Boolean + ): String? { + var connection: HttpURLConnection? = null try { - log.info("GET : $urlText") - val conn = getHttpURLConnection(urlText) - conn.doInput = true - conn.doOutput = false - conn.requestMethod = "GET" - conn.setRequestProperty("User-Agent", "OsmAnd Sharing") - log.info("Response code and message : " + conn.responseCode + " " + conn.responseMessage) - if (conn.responseCode != 200) { - return conn.responseMessage + connection = getHttpURLConnection(url) + connection.setRequestProperty("Accept-Charset", "UTF-8") + connection.setRequestProperty("User-Agent", app.packageName) + connection.connectTimeout = 15000 + if (jsonBody != null && post) { + connection.doInput = true + connection.doOutput = true + connection.useCaches = false + connection.requestMethod = "POST" + + connection.setRequestProperty("Accept", "application/json") + connection.setRequestProperty("Content-Type", "application/json") + connection.setRequestProperty("Content-Length", jsonBody.toByteArray(charset("UTF-8")).size.toString()) + + connection.setFixedLengthStreamingMode(jsonBody.toByteArray(charset("UTF-8")).size) + + val output = BufferedOutputStream(connection.outputStream) + + output.write(jsonBody.toByteArray(charset("UTF-8"))) + output.flush() + output.close() + + } else { + connection.requestMethod = "GET" + connection.connect() } - val inputStream = conn.inputStream - val responseBody = StringBuilder() - responseBody.setLength(0) - if (inputStream != null) { - val bufferedInput = BufferedReader(InputStreamReader(inputStream, "UTF-8")) - var s = bufferedInput.readLine() - var first = true - while (s != null) { - if (first) { - first = false - } else { - responseBody.append("\n") - } - responseBody.append(s) - s = bufferedInput.readLine() + + if (connection.responseCode != HttpURLConnection.HTTP_OK) { + if (toastAllowed) { + val msg = (userOperation + " " + "Failed: " + connection.responseMessage) + log.error(msg) } - inputStream.close() + } else { + val responseBody = StringBuilder() + responseBody.setLength(0) + val i = connection.inputStream + if (i != null) { + val input = BufferedReader(InputStreamReader(i, "UTF-8"), 256) + var s: String? = input.readLine() + var f = true + while (s != null) { + if (!f) { + responseBody.append("\n") + } else { + f = false + } + responseBody.append(s) + s = input.readLine() + } + try { + input.close() + i.close() + } catch (e: Exception) { + // ignore exception + } + + } + return responseBody.toString() + } + } catch (e: NullPointerException) { + if (toastAllowed) { + val msg = (userOperation + " " + "Failed - $e" + ": " + connection?.responseMessage) + log.error(msg) + } + } catch (e: MalformedURLException) { + if (toastAllowed) { + val msg = (userOperation + " " + "Failed - $e" + ": " + connection?.responseMessage) + log.error(msg) } - return responseBody.toString() } catch (e: IOException) { - log.error(e.message, e) - return e.message + if (toastAllowed) { + val msg = (userOperation + " " + "Failed - $e" + ": " + connection?.responseMessage) + log.error(msg) + } + } finally { + connection?.disconnect() } + + return null } @Throws(MalformedURLException::class, IOException::class) diff --git a/OsmAnd-telegram/src/net/osmand/telegram/utils/AndroidUtils.kt b/OsmAnd-telegram/src/net/osmand/telegram/utils/AndroidUtils.kt index a205a55c55..45f9a4d22e 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/utils/AndroidUtils.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/utils/AndroidUtils.kt @@ -6,16 +6,21 @@ import android.content.ContentResolver import android.content.Context import android.content.Intent import android.content.pm.PackageManager +import android.content.res.ColorStateList import android.content.res.Configuration import android.graphics.Color import android.graphics.Paint +import android.graphics.drawable.Drawable +import android.graphics.drawable.StateListDrawable import android.net.Uri import android.os.Build import android.support.annotation.AttrRes import android.support.annotation.ColorInt +import android.support.annotation.ColorRes import android.support.v4.app.ActivityCompat import android.support.v4.app.DialogFragment import android.support.v4.app.FragmentManager +import android.support.v4.content.ContextCompat import android.support.v4.content.FileProvider import android.util.TypedValue import android.util.TypedValue.COMPLEX_UNIT_DIP @@ -131,6 +136,46 @@ object AndroidUtils { return ctx.resources.getDimensionPixelSize(R.dimen.list_popup_window_height) } + fun createPressedColorStateList( + ctx: Context, light: Boolean, + @ColorRes lightNormal: Int, @ColorRes lightPressed: Int, + @ColorRes darkNormal: Int = 0, @ColorRes darkPressed: Int = 0 + ): ColorStateList { + return createColorStateList( + ctx, light, android.R.attr.state_pressed, + lightNormal, lightPressed, darkNormal, darkPressed + ) + } + + fun createColorStateList( + ctx: Context, light: Boolean, state: Int, + @ColorRes lightNormal: Int, @ColorRes lightState: Int, + @ColorRes darkNormal: Int, @ColorRes darkState: Int + ): ColorStateList { + return ColorStateList( + arrayOf(intArrayOf(state), intArrayOf()), + intArrayOf( + ContextCompat.getColor(ctx, if (light) lightState else darkState), + ContextCompat.getColor(ctx, if (light) lightNormal else darkNormal) + ) + ) + } + + fun createPressedStateListDrawable(normal: Drawable, pressed: Drawable): StateListDrawable { + return createStateListDrawable(normal, pressed, android.R.attr.state_pressed) + } + + fun createStateListDrawable( + normal: Drawable, + stateDrawable: Drawable, + state: Int + ): StateListDrawable { + val res = StateListDrawable() + res.addState(intArrayOf(state), stateDrawable) + res.addState(intArrayOf(), normal) + return res + } + @ColorInt fun getAttrColor(ctx: Context, @AttrRes attrId: Int, @ColorInt defaultColor: Int = 0): Int { val ta = ctx.theme.obtainStyledAttributes(intArrayOf(attrId)) diff --git a/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandApiUtils.kt b/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandApiUtils.kt index be10f8f601..019d7603f0 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandApiUtils.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandApiUtils.kt @@ -1,46 +1,95 @@ package net.osmand.telegram.utils import net.osmand.PlatformUtil +import net.osmand.telegram.SHARE_DEVICES_KEY import net.osmand.telegram.TelegramApplication import net.osmand.telegram.TelegramSettings +import org.drinkless.td.libcore.telegram.TdApi import org.json.JSONException import org.json.JSONObject +const val BASE_URL = "https://live.osmand.net" + object OsmandApiUtils { private val log = PlatformUtil.getLog(OsmandApiUtils::class.java) fun updateSharingDevices(app: TelegramApplication, userId: Int) { - AndroidNetworkUtils.sendRequestAsync( - "https://osmand.net/device/send-devices?uid=$userId", + AndroidNetworkUtils.sendRequestAsync(app, "$BASE_URL/device/send-devices?uid=$userId", null, "Get Devices", true, false, object : AndroidNetworkUtils.OnRequestResultListener { - override fun onResult(result: String) { - val list = parseJsonContents(result) - app.settings.updateShareDevicesIds(list) + override fun onResult(result: String?) { + if (result != null) { + val list = parseJsonContents(result) + app.settings.updateShareDevicesIds(list) + } } } ) } + fun createNewDevice( + app: TelegramApplication, + user: TdApi.User, + isBot: Boolean, + deviceName: String, + chatId: Long, + listener: AndroidNetworkUtils.OnRequestResultListener + ) { + val json = getNewDeviceJson(user, isBot, deviceName, chatId) + if (json != null) { + AndroidNetworkUtils.sendRequestAsync(app, "$BASE_URL/device/new", json.toString(), "add Device", true, true, listener) + } + } + + fun parseDeviceBot(deviceJSON: JSONObject): TelegramSettings.DeviceBot? { + return try { + TelegramSettings.DeviceBot().apply { + id = deviceJSON.optLong(TelegramSettings.DeviceBot.DEVICE_ID) + userId = deviceJSON.optLong(TelegramSettings.DeviceBot.USER_ID) + chatId = deviceJSON.optLong(TelegramSettings.DeviceBot.CHAT_ID) + deviceName = deviceJSON.optString(TelegramSettings.DeviceBot.DEVICE_NAME) + externalId = deviceJSON.optString(TelegramSettings.DeviceBot.EXTERNAL_ID) + data = deviceJSON.optString(TelegramSettings.DeviceBot.DATA) + } + } catch (e: JSONException) { + log.error(e.message, e) + null + } + } + fun parseJsonContents(contentsJson: String): List { val list = mutableListOf() try { - val jArray = JSONObject(contentsJson).getJSONArray("devices") + val jArray = JSONObject(contentsJson).getJSONArray(SHARE_DEVICES_KEY) for (i in 0 until jArray.length()) { val deviceJSON = jArray.getJSONObject(i) - val deviceBot = TelegramSettings.DeviceBot().apply { - id = deviceJSON.getLong("id") - userId = deviceJSON.getLong("userId") - chatId = deviceJSON.getLong("chatId") - deviceName = deviceJSON.getString("deviceName") - externalId = deviceJSON.getString("externalId") - data = deviceJSON.getString("data") + val deviceBot = parseDeviceBot(deviceJSON) + if (deviceBot != null) { + list.add(deviceBot) } - list.add(deviceBot) } } catch (e: JSONException) { log.error(e.message, e) } return list } + + private fun getNewDeviceJson(user: TdApi.User, isBot: Boolean, deviceName: String, chatId: Long): JSONObject? { + return try { + val json = JSONObject() + json.put("deviceName", deviceName) + json.put("chatId", chatId) + val jsonUser = JSONObject() + jsonUser.put("id", user.id) + jsonUser.put("firstName", user.firstName) + jsonUser.put("isBot", isBot) + jsonUser.put("lastName", user.lastName) + jsonUser.put("userName", user.username) + jsonUser.put("languageCode", user.languageCode) + json.put("user", jsonUser) + } catch (e: JSONException) { + log.error(e) + null + } + } } \ No newline at end of file