diff --git a/OsmAnd-telegram/AndroidManifest.xml b/OsmAnd-telegram/AndroidManifest.xml index 3b98f7e1e8..a1863ff954 100644 --- a/OsmAnd-telegram/AndroidManifest.xml +++ b/OsmAnd-telegram/AndroidManifest.xml @@ -33,25 +33,14 @@ - + - - - - - - - - \ No newline at end of file diff --git a/OsmAnd-telegram/res/values/strings.xml b/OsmAnd-telegram/res/values/strings.xml index ca33f3ca50..87e368618c 100644 --- a/OsmAnd-telegram/res/values/strings.xml +++ b/OsmAnd-telegram/res/values/strings.xml @@ -27,7 +27,7 @@ Pause Start Stop - OsmAnd Telegram location service + OsmAnd Telegram service OsmAnd logo You need to install free or paid version of OsmAnd first Install OsmAnd diff --git a/OsmAnd-telegram/src/net/osmand/PlatformUtil.java b/OsmAnd-telegram/src/net/osmand/PlatformUtil.java index 06759253ff..9ec83421ae 100644 --- a/OsmAnd-telegram/src/net/osmand/PlatformUtil.java +++ b/OsmAnd-telegram/src/net/osmand/PlatformUtil.java @@ -4,143 +4,143 @@ import org.apache.commons.logging.Log; public class PlatformUtil { - public static String TAG = "net.osmand"; - private static class OsmandLogImplementation implements Log { + public static String TAG = "net.osmand"; - private final String fullName; - private final String name; + private static class OsmandLogImplementation implements Log { - public OsmandLogImplementation(String name){ - this.fullName = name; - this.name = fullName.substring(fullName.lastIndexOf('.') + 1); - } + private final String fullName; + private final String name; - @Override - public void trace(Object message) { - if(isTraceEnabled()){ - android.util.Log.d(TAG, name + " " + message); - } - } + public OsmandLogImplementation(String name) { + this.fullName = name; + this.name = fullName.substring(fullName.lastIndexOf('.') + 1); + } - @Override - public void trace(Object message, Throwable t) { - if(isTraceEnabled()){ - android.util.Log.d(TAG, name + " " + message, t); - } - } + @Override + public void trace(Object message) { + if (isTraceEnabled()) { + android.util.Log.d(TAG, name + " " + message); + } + } - @Override - public void debug(Object message) { - if(isDebugEnabled()){ - android.util.Log.d(TAG, name + " " + message); - } - } + @Override + public void trace(Object message, Throwable t) { + if (isTraceEnabled()) { + android.util.Log.d(TAG, name + " " + message, t); + } + } - @Override - public void debug(Object message, Throwable t) { - if(isDebugEnabled()){ - android.util.Log.d(TAG, name + " " + message, t); - } - } + @Override + public void debug(Object message) { + if (isDebugEnabled()) { + android.util.Log.d(TAG, name + " " + message); + } + } - @Override - public void error(Object message) { - if(isErrorEnabled()){ - android.util.Log.e(TAG, name + " " + message); - } - } + @Override + public void debug(Object message, Throwable t) { + if (isDebugEnabled()) { + android.util.Log.d(TAG, name + " " + message, t); + } + } - @Override - public void error(Object message, Throwable t) { - if(isErrorEnabled()){ - android.util.Log.e(TAG, name + " " + message, t); - } - } + @Override + public void error(Object message) { + if (isErrorEnabled()) { + android.util.Log.e(TAG, name + " " + message); + } + } - @Override - public void fatal(Object message) { - if(isFatalEnabled()){ - android.util.Log.e(TAG, name + " " + message); - } + @Override + public void error(Object message, Throwable t) { + if (isErrorEnabled()) { + android.util.Log.e(TAG, name + " " + message, t); + } + } - } + @Override + public void fatal(Object message) { + if (isFatalEnabled()) { + android.util.Log.e(TAG, name + " " + message); + } - @Override - public void fatal(Object message, Throwable t) { - if(isFatalEnabled()){ - android.util.Log.e(TAG, name + " " + message, t); - } - } + } - @Override - public void info(Object message) { - if(isInfoEnabled()){ - android.util.Log.i(TAG, name + " " + message); - } - } + @Override + public void fatal(Object message, Throwable t) { + if (isFatalEnabled()) { + android.util.Log.e(TAG, name + " " + message, t); + } + } - @Override - public void info(Object message, Throwable t) { - if(isInfoEnabled()){ - android.util.Log.i(TAG, name + " " + message, t); - } - } + @Override + public void info(Object message) { + if (isInfoEnabled()) { + android.util.Log.i(TAG, name + " " + message); + } + } - @Override - public boolean isTraceEnabled() { - return android.util.Log.isLoggable(TAG, android.util.Log.VERBOSE); - } + @Override + public void info(Object message, Throwable t) { + if (isInfoEnabled()) { + android.util.Log.i(TAG, name + " " + message, t); + } + } + + @Override + public boolean isTraceEnabled() { + return android.util.Log.isLoggable(TAG, android.util.Log.VERBOSE); + } + @Override + public boolean isDebugEnabled() { + // For debug purposes always true + // return android.util.Log.isLoggable(TAG, android.util.Log.DEBUG); + return true; + } - @Override - public boolean isDebugEnabled() { - // For debug purposes always true - // return android.util.Log.isLoggable(TAG, android.util.Log.DEBUG); - return true; - } + @Override + public boolean isErrorEnabled() { + return android.util.Log.isLoggable(TAG, android.util.Log.ERROR); + } - @Override - public boolean isErrorEnabled() { - return android.util.Log.isLoggable(TAG, android.util.Log.ERROR); - } + @Override + public boolean isFatalEnabled() { + return android.util.Log.isLoggable(TAG, android.util.Log.ERROR); + } - @Override - public boolean isFatalEnabled() { - return android.util.Log.isLoggable(TAG, android.util.Log.ERROR); - } + @Override + public boolean isInfoEnabled() { + return android.util.Log.isLoggable(TAG, android.util.Log.INFO); + } - @Override - public boolean isInfoEnabled() { - return android.util.Log.isLoggable(TAG, android.util.Log.INFO); - } + @Override + public boolean isWarnEnabled() { + return android.util.Log.isLoggable(TAG, android.util.Log.WARN); + } - @Override - public boolean isWarnEnabled() { - return android.util.Log.isLoggable(TAG, android.util.Log.WARN); - } + @Override + public void warn(Object message) { + if (isWarnEnabled()) { + android.util.Log.w(TAG, name + " " + message); + } + } - @Override - public void warn(Object message) { - if(isWarnEnabled()){ - android.util.Log.w(TAG, name + " " + message); - } - } + @Override + public void warn(Object message, Throwable t) { + if (isWarnEnabled()) { + android.util.Log.w(TAG, name + " " + message, t); + } + } + } - @Override - public void warn(Object message, Throwable t) { - if(isWarnEnabled()){ - android.util.Log.w(TAG, name + " " + message, t); - } - } - } + public static Log getLog(String name) { + return new OsmandLogImplementation(name); + } - public static Log getLog(String name){ - return new OsmandLogImplementation(name); - } - - public static Log getLog(Class cl){ - return getLog(cl.getName()); - } + public static Log getLog(Class cl) { + return getLog(cl.getName()); + } } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/LoginDialogFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/LoginDialogFragment.kt index 66b840bd13..41abbe5289 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/LoginDialogFragment.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/LoginDialogFragment.kt @@ -17,182 +17,182 @@ import net.osmand.PlatformUtil class LoginDialogFragment : DialogFragment() { - companion object { + companion object { - private const val TAG = "LoginDialogFragment" - private val LOG = PlatformUtil.getLog(LoginDialogFragment::class.java) + private const val TAG = "LoginDialogFragment" + private val LOG = PlatformUtil.getLog(LoginDialogFragment::class.java) - private const val ENTER_PHONE_NUMBER_PARAM_KEY: String = "enter_phone_number_param_key" - private const val ENTER_CODE_PARAM_KEY = "enter_code_param_key" - private const val ENTER_PASSWORD_PARAM_KEY = "enter_password_param_key" - private const val SHOW_PROGRESS_PARAM_KEY = "show_progress_param_key" + private const val ENTER_PHONE_NUMBER_PARAM_KEY: String = "enter_phone_number_param_key" + private const val ENTER_CODE_PARAM_KEY = "enter_code_param_key" + private const val ENTER_PASSWORD_PARAM_KEY = "enter_password_param_key" + private const val SHOW_PROGRESS_PARAM_KEY = "show_progress_param_key" - fun showDialog(fragmentManager: FragmentManager, vararg loginDialogType: LoginDialogType) { - try { - var fragment = getFragment(fragmentManager) - if (fragment == null) { - fragment = LoginDialogFragment() - val args = Bundle() - for (t in loginDialogType) { - args.putBoolean(t.paramKey, true) - } - fragment.arguments = args - fragment.show(fragmentManager, TAG) - } else { - fragment.updateDialog(*loginDialogType) - } - } catch (e: RuntimeException) { - LOG.error(e) - } - } + fun showDialog(fragmentManager: FragmentManager, vararg loginDialogType: LoginDialogType) { + try { + var fragment = getFragment(fragmentManager) + if (fragment == null) { + fragment = LoginDialogFragment() + val args = Bundle() + for (t in loginDialogType) { + args.putBoolean(t.paramKey, true) + } + fragment.arguments = args + fragment.show(fragmentManager, TAG) + } else { + fragment.updateDialog(*loginDialogType) + } + } catch (e: RuntimeException) { + LOG.error(e) + } + } - fun dismiss(fragmentManager: FragmentManager) { - val loginDialogFragment = getFragment(fragmentManager) - loginDialogFragment?.dismissedManually = true - loginDialogFragment?.dismiss() - } + fun dismiss(fragmentManager: FragmentManager) { + val loginDialogFragment = getFragment(fragmentManager) + loginDialogFragment?.dismissedManually = true + loginDialogFragment?.dismiss() + } - private fun getFragment(fragmentManager: FragmentManager): LoginDialogFragment? { - return fragmentManager.findFragmentByTag(TAG) as LoginDialogFragment? - } - } + private fun getFragment(fragmentManager: FragmentManager): LoginDialogFragment? { + return fragmentManager.findFragmentByTag(TAG) as LoginDialogFragment? + } + } - private var loginDialogActiveTypes: Set? = null + private var loginDialogActiveTypes: Set? = null - private var dismissedManually = false + private var dismissedManually = false - enum class LoginDialogType(val paramKey: String, val viewId: Int, val editorId: Int) { - ENTER_PHONE_NUMBER(ENTER_PHONE_NUMBER_PARAM_KEY, R.id.enterPhoneNumberLayout, R.id.phoneNumberEditText), - ENTER_CODE(ENTER_CODE_PARAM_KEY, R.id.enterCodeLayout, R.id.codeEditText), - ENTER_PASSWORD(ENTER_PASSWORD_PARAM_KEY, R.id.enterPasswordLayout, R.id.passwordEditText), - SHOW_PROGRESS(SHOW_PROGRESS_PARAM_KEY, R.id.progressLayout, 0); - } + enum class LoginDialogType(val paramKey: String, val viewId: Int, val editorId: Int) { + ENTER_PHONE_NUMBER(ENTER_PHONE_NUMBER_PARAM_KEY, R.id.enterPhoneNumberLayout, R.id.phoneNumberEditText), + ENTER_CODE(ENTER_CODE_PARAM_KEY, R.id.enterCodeLayout, R.id.codeEditText), + ENTER_PASSWORD(ENTER_PASSWORD_PARAM_KEY, R.id.enterPasswordLayout, R.id.passwordEditText), + SHOW_PROGRESS(SHOW_PROGRESS_PARAM_KEY, R.id.progressLayout, 0); + } - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(DialogFragment.STYLE_NO_FRAME, R.style.AppTheme_NoActionbar) - val activity = requireActivity() - val window = activity.window - window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) - } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(DialogFragment.STYLE_NO_FRAME, R.style.AppTheme_NoActionbar) + val activity = requireActivity() + val window = activity.window + window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) + } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val loginDialogActiveTypes: MutableSet = HashSet() + val loginDialogActiveTypes: MutableSet = HashSet() - val args = savedInstanceState ?: arguments - if (args != null) { - for (t in LoginDialogType.values()) { - if (args.getBoolean(t.paramKey, false)) { - loginDialogActiveTypes.add(t) - } - } - } + val args = savedInstanceState ?: arguments + if (args != null) { + for (t in LoginDialogType.values()) { + if (args.getBoolean(t.paramKey, false)) { + loginDialogActiveTypes.add(t) + } + } + } - this.loginDialogActiveTypes = loginDialogActiveTypes + this.loginDialogActiveTypes = loginDialogActiveTypes - val view = inflater.inflate(R.layout.login_dialog, container) - buildDialog(view) - return view - } + val view = inflater.inflate(R.layout.login_dialog, container) + buildDialog(view) + return view + } - override fun onDismiss(dialog: DialogInterface?) { - super.onDismiss(dialog) - if (!dismissedManually) { - getMainActivity()?.closeTelegram() - } - } + override fun onDismiss(dialog: DialogInterface?) { + super.onDismiss(dialog) + if (!dismissedManually) { + getMainActivity()?.closeTelegram() + } + } - private fun buildDialog(view: View?) { - val loginDialogActiveTypes = this.loginDialogActiveTypes - var hasProgress = false - var focusRequested = false - for (t in LoginDialogType.values()) { - val layout: View? = view?.findViewById(t.viewId) - val contains = loginDialogActiveTypes?.contains(t) ?: false - when { - contains -> { - if (t == LoginDialogType.SHOW_PROGRESS) { - hasProgress = true - } - if (layout != null) { - layout.visibility = View.VISIBLE - val editText: EditText? = layout.findViewById(t.editorId) - if (editText != null) { - editText.setOnEditorActionListener { _, actionId, _ -> - if (actionId == EditorInfo.IME_ACTION_DONE) { - applyAuthParam(t, editText.text.toString()) - return@setOnEditorActionListener true - } - false - } - if (!focusRequested) { - editText.requestFocus() - AndroidUtils.softKeyboardDelayed(editText) - focusRequested = true - } - } - } - } - else -> layout?.visibility = View.GONE - } - } - val continueButton: Button? = view?.findViewById(R.id.continueButton) - if (continueButton != null) { - continueButton.isEnabled = !hasProgress - if (hasProgress) { - continueButton.setOnClickListener(null) - } else { - continueButton.setOnClickListener { - for (t in LoginDialogType.values()) { - val layout: View? = view.findViewById(t.viewId) - val contains = loginDialogActiveTypes?.contains(t) ?: false - if (contains && layout != null) { - val editText: EditText? = layout.findViewById(t.editorId) - if (editText != null) { - applyAuthParam(t, editText.text.toString()) - } - } - } - } - } - } - val cancelButton: Button? = view?.findViewById(R.id.calcelButton) - cancelButton?.setOnClickListener { - dismiss() - } - } + private fun buildDialog(view: View?) { + val loginDialogActiveTypes = this.loginDialogActiveTypes + var hasProgress = false + var focusRequested = false + for (t in LoginDialogType.values()) { + val layout: View? = view?.findViewById(t.viewId) + val contains = loginDialogActiveTypes?.contains(t) ?: false + when { + contains -> { + if (t == LoginDialogType.SHOW_PROGRESS) { + hasProgress = true + } + if (layout != null) { + layout.visibility = View.VISIBLE + val editText: EditText? = layout.findViewById(t.editorId) + if (editText != null) { + editText.setOnEditorActionListener { _, actionId, _ -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + applyAuthParam(t, editText.text.toString()) + return@setOnEditorActionListener true + } + false + } + if (!focusRequested) { + editText.requestFocus() + AndroidUtils.softKeyboardDelayed(editText) + focusRequested = true + } + } + } + } + else -> layout?.visibility = View.GONE + } + } + val continueButton: Button? = view?.findViewById(R.id.continueButton) + if (continueButton != null) { + continueButton.isEnabled = !hasProgress + if (hasProgress) { + continueButton.setOnClickListener(null) + } else { + continueButton.setOnClickListener { + for (t in LoginDialogType.values()) { + val layout: View? = view.findViewById(t.viewId) + val contains = loginDialogActiveTypes?.contains(t) ?: false + if (contains && layout != null) { + val editText: EditText? = layout.findViewById(t.editorId) + if (editText != null) { + applyAuthParam(t, editText.text.toString()) + } + } + } + } + } + } + val cancelButton: Button? = view?.findViewById(R.id.calcelButton) + cancelButton?.setOnClickListener { + dismiss() + } + } - private fun applyAuthParam(t: LoginDialogType, value: String) { - getMainActivity()?.applyAuthParam(this, t, value) - } + private fun applyAuthParam(t: LoginDialogType, value: String) { + getMainActivity()?.applyAuthParam(this, t, value) + } - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - val loginDialogActiveTypes = this.loginDialogActiveTypes - if (loginDialogActiveTypes != null) { - for (t in loginDialogActiveTypes) { - outState.putBoolean(t.paramKey, true) - } - } - } + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + val loginDialogActiveTypes = this.loginDialogActiveTypes + if (loginDialogActiveTypes != null) { + for (t in loginDialogActiveTypes) { + outState.putBoolean(t.paramKey, true) + } + } + } - private fun getMainActivity(): MainActivity? { - val activity = this.activity - return if (activity != null) { - activity as MainActivity - } else { - null - } - } + private fun getMainActivity(): MainActivity? { + val activity = this.activity + return if (activity != null) { + activity as MainActivity + } else { + null + } + } - fun updateDialog(vararg loginDialogType: LoginDialogType) { - val loginDialogActiveTypes: MutableSet = HashSet() - for (t in loginDialogType) { - loginDialogActiveTypes.add(t) - } - this.loginDialogActiveTypes = loginDialogActiveTypes + fun updateDialog(vararg loginDialogType: LoginDialogType) { + val loginDialogActiveTypes: MutableSet = HashSet() + for (t in loginDialogType) { + loginDialogActiveTypes.add(t) + } + this.loginDialogActiveTypes = loginDialogActiveTypes - buildDialog(view) - } + buildDialog(view) + } } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/MainActivity.kt b/OsmAnd-telegram/src/net/osmand/telegram/MainActivity.kt index 66e0356a30..4828358de2 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/MainActivity.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/MainActivity.kt @@ -23,370 +23,376 @@ import org.drinkless.td.libcore.telegram.TdApi class MainActivity : AppCompatActivity(), TelegramListener { - companion object { - private const val PERMISSION_REQUEST_LOCATION = 1 + companion object { + private const val PERMISSION_REQUEST_LOCATION = 1 - private const val LOGIN_MENU_ID = 0 - private const val LOGOUT_MENU_ID = 1 - private const val PROGRESS_MENU_ID = 2 - } + private const val LOGIN_MENU_ID = 0 + private const val LOGOUT_MENU_ID = 1 + private const val PROGRESS_MENU_ID = 2 + } - private val log = PlatformUtil.getLog(TelegramHelper::class.java) + private val log = PlatformUtil.getLog(TelegramHelper::class.java) - private var telegramAuthorizationRequestHandler: TelegramAuthorizationRequestHandler? = null - private var paused: Boolean = false + private var telegramAuthorizationRequestHandler: TelegramAuthorizationRequestHandler? = null + private var paused: Boolean = false - private lateinit var chatsView: RecyclerView - private lateinit var chatViewAdapter: ChatsAdapter - private lateinit var chatViewManager: RecyclerView.LayoutManager + private lateinit var chatsView: RecyclerView + private lateinit var chatViewAdapter: ChatsAdapter + private lateinit var chatViewManager: RecyclerView.LayoutManager - private val app: TelegramApplication - get() = application as TelegramApplication + private val app: TelegramApplication + get() = application as TelegramApplication - private val telegramHelper get() = app.telegramHelper - private val osmandHelper get() = app.osmandHelper - private val settings get() = app.settings + private val telegramHelper get() = app.telegramHelper + private val osmandHelper get() = app.osmandHelper + private val settings get() = app.settings - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) - paused = false + paused = false - chatViewManager = LinearLayoutManager(this) - chatViewAdapter = ChatsAdapter() + chatViewManager = LinearLayoutManager(this) + chatViewAdapter = ChatsAdapter() - chatsView = findViewById(R.id.groups_view).apply { - //setHasFixedSize(true) + chatsView = findViewById(R.id.groups_view).apply { + //setHasFixedSize(true) - // use a linear layout manager - layoutManager = chatViewManager + // use a linear layout manager + layoutManager = chatViewManager - // specify an viewAdapter (see also next example) - adapter = chatViewAdapter + // specify an viewAdapter (see also next example) + adapter = chatViewAdapter - } + } - telegramAuthorizationRequestHandler = telegramHelper.setTelegramAuthorizationRequestHandler(object : TelegramAuthorizationRequestListener { - override fun onRequestTelegramAuthenticationParameter(parameterType: TelegramAuthenticationParameterType) { - runOnUi { - showLoginDialog(parameterType) - } - } + telegramAuthorizationRequestHandler = telegramHelper.setTelegramAuthorizationRequestHandler(object : TelegramAuthorizationRequestListener { + override fun onRequestTelegramAuthenticationParameter(parameterType: TelegramAuthenticationParameterType) { + runOnUi { + showLoginDialog(parameterType) + } + } - override fun onTelegramAuthorizationRequestError(code: Int, message: String) { - runOnUi { - Toast.makeText(this@MainActivity, "$code - $message", Toast.LENGTH_LONG).show() - } - } - }) - telegramHelper.listener = this - telegramHelper.init() - } + override fun onTelegramAuthorizationRequestError(code: Int, message: String) { + runOnUi { + Toast.makeText(this@MainActivity, "$code - $message", Toast.LENGTH_LONG).show() + } + } + }) + telegramHelper.listener = this + telegramHelper.init() + } - override fun onResume() { - super.onResume() - paused = false + override fun onResume() { + super.onResume() + paused = false - invalidateOptionsMenu() - updateTitle() + invalidateOptionsMenu() + updateTitle() - if (settings.hasAnyChatToShareLocation() && !AndroidUtils.isLocationPermissionAvailable(this)) { - requestLocationPermission() - } else if (settings.hasAnyChatToShowOnMap() && osmandHelper.initialized && !osmandHelper.isOsmandBound()) { - showOsmandMissingDialog() - } - } + if (settings.hasAnyChatToShareLocation() && !AndroidUtils.isLocationPermissionAvailable(this)) { + requestLocationPermission() + } else if (settings.hasAnyChatToShowOnMap() && osmandHelper.initialized && !osmandHelper.isOsmandBound()) { + showOsmandMissingDialog() + } + } - override fun onPause() { - super.onPause() - paused = true - } + override fun onPause() { + super.onPause() + paused = true + } - override fun onStop() { - super.onStop() - settings.save() - } + override fun onStop() { + super.onStop() + settings.save() + } - override fun onTelegramStatusChanged(prevTelegramAuthorizationState: TelegramAuthorizationState, - newTelegramAuthorizationState: TelegramAuthorizationState) { - runOnUi { - val fm = supportFragmentManager - when (newTelegramAuthorizationState) { - TelegramAuthorizationState.READY, - TelegramAuthorizationState.CLOSED, - TelegramAuthorizationState.UNKNOWN -> LoginDialogFragment.dismiss(fm) - else -> Unit - } - invalidateOptionsMenu() - updateTitle() + override fun onDestroy() { + super.onDestroy() - when (newTelegramAuthorizationState) { - TelegramAuthorizationState.READY -> { - updateChatsList() - } - TelegramAuthorizationState.CLOSED, - TelegramAuthorizationState.UNKNOWN -> { - chatViewAdapter.chats = emptyList() - } - else -> Unit - } - } - } + app.cleanupResources() + } - override fun onTelegramChatsRead() { - runOnUi { - removeNonexistingChatsFromSettings() - updateChatsList() - } - } + override fun onTelegramStatusChanged(prevTelegramAuthorizationState: TelegramAuthorizationState, + newTelegramAuthorizationState: TelegramAuthorizationState) { + runOnUi { + val fm = supportFragmentManager + when (newTelegramAuthorizationState) { + TelegramAuthorizationState.READY, + TelegramAuthorizationState.CLOSED, + TelegramAuthorizationState.UNKNOWN -> LoginDialogFragment.dismiss(fm) + else -> Unit + } + invalidateOptionsMenu() + updateTitle() - override fun onTelegramChatsChanged() { - runOnUi { - updateChatsList() - } - } + when (newTelegramAuthorizationState) { + TelegramAuthorizationState.READY -> { + updateChatsList() + } + TelegramAuthorizationState.CLOSED, + TelegramAuthorizationState.UNKNOWN -> { + chatViewAdapter.chats = emptyList() + } + else -> Unit + } + } + } - override fun onTelegramError(code: Int, message: String) { - runOnUi { - Toast.makeText(this@MainActivity, "$code - $message", Toast.LENGTH_LONG).show() - } - } + override fun onTelegramChatsRead() { + runOnUi { + removeNonexistingChatsFromSettings() + updateChatsList() + } + } - override fun onSendLiveLicationError(code: Int, message: String) { - log.error("Send live location error: $code - $message") - app.isInternetConnectionAvailable(true) - } + override fun onTelegramChatsChanged() { + runOnUi { + updateChatsList() + } + } - private fun removeNonexistingChatsFromSettings() { - val presentChatTitles = telegramHelper.getChatTitles() - settings.removeNonexistingChats(presentChatTitles) - } + override fun onTelegramError(code: Int, message: String) { + runOnUi { + Toast.makeText(this@MainActivity, "$code - $message", Toast.LENGTH_LONG).show() + } + } - private fun updateChatsList() { - val chatList = telegramHelper.getChatList() - val chats: MutableList = mutableListOf() - for (orderedChat in chatList) { - val chat = telegramHelper.getChat(orderedChat.chatId) - if (chat != null) { - chats.add(chat) - } - } - chatViewAdapter.chats = chats - } + override fun onSendLiveLicationError(code: Int, message: String) { + log.error("Send live location error: $code - $message") + app.isInternetConnectionAvailable(true) + } - fun logoutTelegram(silent: Boolean = false) { - if (telegramHelper.getTelegramAuthorizationState() == TelegramAuthorizationState.READY) { - telegramHelper.logout() - } else { - invalidateOptionsMenu() - updateTitle() - if (!silent) { - Toast.makeText(this, R.string.not_logged_in, Toast.LENGTH_SHORT).show() - } - } - } + private fun removeNonexistingChatsFromSettings() { + val presentChatTitles = telegramHelper.getChatTitles() + settings.removeNonexistingChats(presentChatTitles) + } - fun closeTelegram() { - telegramHelper.close() - } + private fun updateChatsList() { + val chatList = telegramHelper.getChatList() + val chats: MutableList = mutableListOf() + for (orderedChat in chatList) { + val chat = telegramHelper.getChat(orderedChat.chatId) + if (chat != null) { + chats.add(chat) + } + } + chatViewAdapter.chats = chats + } - private fun runOnUi(action: (() -> Unit)) { - if (!paused) { - runOnUiThread(action) - } - } + fun logoutTelegram(silent: Boolean = false) { + if (telegramHelper.getTelegramAuthorizationState() == TelegramAuthorizationState.READY) { + telegramHelper.logout() + } else { + invalidateOptionsMenu() + updateTitle() + if (!silent) { + Toast.makeText(this, R.string.not_logged_in, Toast.LENGTH_SHORT).show() + } + } + } - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - return when (item?.itemId) { - LOGIN_MENU_ID -> { - telegramHelper.init() - true - } - LOGOUT_MENU_ID -> { - logoutTelegram() - true - } - else -> super.onOptionsItemSelected(item) - } - } + fun closeTelegram() { + telegramHelper.close() + } - override fun onCreateOptionsMenu(menu: Menu?): Boolean { - if (menu != null) { - menu.clear() - when (telegramHelper.getTelegramAuthorizationState()) { - TelegramAuthorizationState.UNKNOWN, - TelegramAuthorizationState.WAIT_PARAMETERS, - TelegramAuthorizationState.WAIT_PHONE_NUMBER, - TelegramAuthorizationState.WAIT_CODE, - TelegramAuthorizationState.WAIT_PASSWORD, - TelegramAuthorizationState.LOGGING_OUT, - TelegramAuthorizationState.CLOSING -> createProgressMenuItem(menu) - TelegramAuthorizationState.READY -> createMenuItem(menu, LOGOUT_MENU_ID, R.string.shared_string_logout, - MenuItem.SHOW_AS_ACTION_WITH_TEXT or MenuItem.SHOW_AS_ACTION_ALWAYS) - TelegramAuthorizationState.CLOSED -> createMenuItem(menu, LOGIN_MENU_ID, R.string.shared_string_login, - MenuItem.SHOW_AS_ACTION_WITH_TEXT or MenuItem.SHOW_AS_ACTION_ALWAYS) - } - } - return super.onCreateOptionsMenu(menu) - } + private fun runOnUi(action: (() -> Unit)) { + if (!paused) { + runOnUiThread(action) + } + } - private fun createMenuItem(m: Menu, id: Int, titleRes: Int, menuItemType: Int): MenuItem { - val menuItem = m.add(0, id, 0, titleRes) - menuItem.setOnMenuItemClickListener { item -> onOptionsItemSelected(item) } - menuItem.setShowAsAction(menuItemType) - return menuItem - } + override fun onOptionsItemSelected(item: MenuItem?): Boolean { + return when (item?.itemId) { + LOGIN_MENU_ID -> { + telegramHelper.init() + true + } + LOGOUT_MENU_ID -> { + logoutTelegram() + true + } + else -> super.onOptionsItemSelected(item) + } + } - private fun createProgressMenuItem(m: Menu): MenuItem { + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + if (menu != null) { + menu.clear() + when (telegramHelper.getTelegramAuthorizationState()) { + TelegramAuthorizationState.UNKNOWN, + TelegramAuthorizationState.WAIT_PARAMETERS, + TelegramAuthorizationState.WAIT_PHONE_NUMBER, + TelegramAuthorizationState.WAIT_CODE, + TelegramAuthorizationState.WAIT_PASSWORD, + TelegramAuthorizationState.LOGGING_OUT, + TelegramAuthorizationState.CLOSING -> createProgressMenuItem(menu) + TelegramAuthorizationState.READY -> createMenuItem(menu, LOGOUT_MENU_ID, R.string.shared_string_logout, + MenuItem.SHOW_AS_ACTION_WITH_TEXT or MenuItem.SHOW_AS_ACTION_ALWAYS) + TelegramAuthorizationState.CLOSED -> createMenuItem(menu, LOGIN_MENU_ID, R.string.shared_string_login, + MenuItem.SHOW_AS_ACTION_WITH_TEXT or MenuItem.SHOW_AS_ACTION_ALWAYS) + } + } + return super.onCreateOptionsMenu(menu) + } - val menuItem = m.add(0, PROGRESS_MENU_ID, 0, "") - menuItem.actionView = layoutInflater.inflate(R.layout.action_progress_bar, null) - menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS) - return menuItem - } + private fun createMenuItem(m: Menu, id: Int, titleRes: Int, menuItemType: Int): MenuItem { + val menuItem = m.add(0, id, 0, titleRes) + menuItem.setOnMenuItemClickListener { item -> onOptionsItemSelected(item) } + menuItem.setShowAsAction(menuItemType) + return menuItem + } - private fun updateTitle() { - title = when (telegramHelper.getTelegramAuthorizationState()) { + private fun createProgressMenuItem(m: Menu): MenuItem { - TelegramAuthorizationState.UNKNOWN, - TelegramAuthorizationState.WAIT_PHONE_NUMBER, - TelegramAuthorizationState.WAIT_CODE, - TelegramAuthorizationState.WAIT_PASSWORD, - TelegramAuthorizationState.READY, - TelegramAuthorizationState.CLOSED -> getString(R.string.app_name) + val menuItem = m.add(0, PROGRESS_MENU_ID, 0, "") + menuItem.actionView = layoutInflater.inflate(R.layout.action_progress_bar, null) + menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS) + return menuItem + } - TelegramAuthorizationState.WAIT_PARAMETERS -> getString(R.string.initialization) + "..." - TelegramAuthorizationState.LOGGING_OUT -> getString(R.string.logging_out) + "..." - TelegramAuthorizationState.CLOSING -> getString(R.string.closing) + "..." - } - } + private fun updateTitle() { + title = when (telegramHelper.getTelegramAuthorizationState()) { - private fun showLoginDialog(telegramAuthenticationParameterType: TelegramAuthenticationParameterType) { - when (telegramAuthenticationParameterType) { - TelegramAuthenticationParameterType.PHONE_NUMBER -> LoginDialogFragment.showDialog(supportFragmentManager, LoginDialogType.ENTER_PHONE_NUMBER) - TelegramAuthenticationParameterType.CODE -> LoginDialogFragment.showDialog(supportFragmentManager, LoginDialogType.ENTER_CODE) - TelegramAuthenticationParameterType.PASSWORD -> LoginDialogFragment.showDialog(supportFragmentManager, LoginDialogType.ENTER_PASSWORD) - } - } + TelegramAuthorizationState.UNKNOWN, + TelegramAuthorizationState.WAIT_PHONE_NUMBER, + TelegramAuthorizationState.WAIT_CODE, + TelegramAuthorizationState.WAIT_PASSWORD, + TelegramAuthorizationState.READY, + TelegramAuthorizationState.CLOSED -> getString(R.string.app_name) - fun applyAuthParam(loginDialogFragment: LoginDialogFragment?, loginDialogType: LoginDialogType, text: String) { - loginDialogFragment?.updateDialog(LoginDialogType.SHOW_PROGRESS) - when (loginDialogType) { - LoginDialogType.ENTER_PHONE_NUMBER -> telegramAuthorizationRequestHandler?.applyAuthenticationParameter(TelegramAuthenticationParameterType.PHONE_NUMBER, text) - LoginDialogType.ENTER_CODE -> telegramAuthorizationRequestHandler?.applyAuthenticationParameter(TelegramAuthenticationParameterType.CODE, text) - LoginDialogType.ENTER_PASSWORD -> telegramAuthorizationRequestHandler?.applyAuthenticationParameter(TelegramAuthenticationParameterType.PASSWORD, text) - else -> Unit - } - } + TelegramAuthorizationState.WAIT_PARAMETERS -> getString(R.string.initialization) + "..." + TelegramAuthorizationState.LOGGING_OUT -> getString(R.string.logging_out) + "..." + TelegramAuthorizationState.CLOSING -> getString(R.string.closing) + "..." + } + } - private fun requestLocationPermission() { - ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), PERMISSION_REQUEST_LOCATION) - } + private fun showLoginDialog(telegramAuthenticationParameterType: TelegramAuthenticationParameterType) { + when (telegramAuthenticationParameterType) { + TelegramAuthenticationParameterType.PHONE_NUMBER -> LoginDialogFragment.showDialog(supportFragmentManager, LoginDialogType.ENTER_PHONE_NUMBER) + TelegramAuthenticationParameterType.CODE -> LoginDialogFragment.showDialog(supportFragmentManager, LoginDialogType.ENTER_CODE) + TelegramAuthenticationParameterType.PASSWORD -> LoginDialogFragment.showDialog(supportFragmentManager, LoginDialogType.ENTER_PASSWORD) + } + } - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - when (requestCode) { - PERMISSION_REQUEST_LOCATION -> { - if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { - if (settings.hasAnyChatToShareLocation()) { - app.shareLocationHelper.startSharingLocation() - } - } - if (settings.hasAnyChatToShowOnMap() && osmandHelper.initialized && !osmandHelper.isOsmandBound()) { - showOsmandMissingDialog() - } - } - } - } + fun applyAuthParam(loginDialogFragment: LoginDialogFragment?, loginDialogType: LoginDialogType, text: String) { + loginDialogFragment?.updateDialog(LoginDialogType.SHOW_PROGRESS) + when (loginDialogType) { + LoginDialogType.ENTER_PHONE_NUMBER -> telegramAuthorizationRequestHandler?.applyAuthenticationParameter(TelegramAuthenticationParameterType.PHONE_NUMBER, text) + LoginDialogType.ENTER_CODE -> telegramAuthorizationRequestHandler?.applyAuthenticationParameter(TelegramAuthenticationParameterType.CODE, text) + LoginDialogType.ENTER_PASSWORD -> telegramAuthorizationRequestHandler?.applyAuthenticationParameter(TelegramAuthenticationParameterType.PASSWORD, text) + else -> Unit + } + } - fun showOsmandMissingDialog() { - OsmandMissingDialogFragment().show(supportFragmentManager, null) - } + private fun requestLocationPermission() { + ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), PERMISSION_REQUEST_LOCATION) + } - class OsmandMissingDialogFragment : DialogFragment() { + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + when (requestCode) { + PERMISSION_REQUEST_LOCATION -> { + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + if (settings.hasAnyChatToShareLocation()) { + app.shareLocationHelper.startSharingLocation() + } + } + if (settings.hasAnyChatToShowOnMap() && osmandHelper.initialized && !osmandHelper.isOsmandBound()) { + showOsmandMissingDialog() + } + } + } + } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val builder = AlertDialog.Builder(requireContext()) - builder.setView(R.layout.install_osmand_dialog) - .setNegativeButton("Cancel", null) - .setPositiveButton("Install", { _, _ -> - val intent = Intent() - intent.data = Uri.parse("market://details?id=net.osmand.plus") - startActivity(intent) - }) - return builder.create() - } - } + fun showOsmandMissingDialog() { + OsmandMissingDialogFragment().show(supportFragmentManager, null) + } - inner class ChatsAdapter : - RecyclerView.Adapter() { + class OsmandMissingDialogFragment : DialogFragment() { - var chats: List = emptyList() - set(value) { - field = value - notifyDataSetChanged() - } + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val builder = AlertDialog.Builder(requireContext()) + builder.setView(R.layout.install_osmand_dialog) + .setNegativeButton("Cancel", null) + .setPositiveButton("Install", { _, _ -> + val intent = Intent() + intent.data = Uri.parse("market://details?id=net.osmand.plus") + startActivity(intent) + }) + return builder.create() + } + } - inner class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) { - val icon: AppCompatImageView? = view.findViewById(R.id.icon) - val groupName: AppCompatTextView? = view.findViewById(R.id.name) - val shareLocationSwitch: SwitchCompat? = view.findViewById(R.id.share_location_switch) - val showOnMapSwitch: SwitchCompat? = view.findViewById(R.id.show_on_map_switch) - } + inner class ChatsAdapter : + RecyclerView.Adapter() { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatsAdapter.ViewHolder { - val view = LayoutInflater.from(parent.context).inflate(R.layout.chat_list_item, parent, false) - return ViewHolder(view) - } + var chats: List = emptyList() + set(value) { + field = value + notifyDataSetChanged() + } - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val chatTitle = chats[position].title - holder.groupName?.text = chatTitle + inner class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) { + val icon: AppCompatImageView? = view.findViewById(R.id.icon) + val groupName: AppCompatTextView? = view.findViewById(R.id.name) + val shareLocationSwitch: SwitchCompat? = view.findViewById(R.id.share_location_switch) + val showOnMapSwitch: SwitchCompat? = view.findViewById(R.id.show_on_map_switch) + } - holder.shareLocationSwitch?.setOnCheckedChangeListener(null) - holder.shareLocationSwitch?.isChecked = settings.isSharingLocationToChat(chatTitle) - holder.shareLocationSwitch?.setOnCheckedChangeListener { view, isChecked -> - settings.shareLocationToChat(chatTitle, isChecked) - if (settings.hasAnyChatToShareLocation()) { - if (!AndroidUtils.isLocationPermissionAvailable(view.context)) { - if (isChecked) { - requestLocationPermission() - } - } else { - app.shareLocationHelper.startSharingLocation() - } - } else { - app.shareLocationHelper.stopSharingLocation() - } - } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatsAdapter.ViewHolder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.chat_list_item, parent, false) + return ViewHolder(view) + } - holder.showOnMapSwitch?.setOnCheckedChangeListener(null) - holder.showOnMapSwitch?.isChecked = settings.isShowingChatOnMap(chatTitle) - holder.showOnMapSwitch?.setOnCheckedChangeListener { _, isChecked -> - settings.showChatOnMap(chatTitle, isChecked) - if (settings.hasAnyChatToShowOnMap()) { - if (osmandHelper.initialized && !osmandHelper.isOsmandBound()) { - if (isChecked) { - showOsmandMissingDialog() - } - } else { - if (isChecked) { - app.showLocationHelper.showChatMessages(chatTitle) - } else { - app.showLocationHelper.hideChatMessages(chatTitle) - } - app.showLocationHelper.startShowingLocation() - } - } else { - app.showLocationHelper.stopShowingLocation() - if (!isChecked) { - app.showLocationHelper.hideChatMessages(chatTitle) - } - } - } - } + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val chatTitle = chats[position].title + holder.groupName?.text = chatTitle - override fun getItemCount() = chats.size - } + holder.shareLocationSwitch?.setOnCheckedChangeListener(null) + holder.shareLocationSwitch?.isChecked = settings.isSharingLocationToChat(chatTitle) + holder.shareLocationSwitch?.setOnCheckedChangeListener { view, isChecked -> + settings.shareLocationToChat(chatTitle, isChecked) + if (settings.hasAnyChatToShareLocation()) { + if (!AndroidUtils.isLocationPermissionAvailable(view.context)) { + if (isChecked) { + requestLocationPermission() + } + } else { + app.shareLocationHelper.startSharingLocation() + } + } else { + app.shareLocationHelper.stopSharingLocation() + } + } + + holder.showOnMapSwitch?.setOnCheckedChangeListener(null) + holder.showOnMapSwitch?.isChecked = settings.isShowingChatOnMap(chatTitle) + holder.showOnMapSwitch?.setOnCheckedChangeListener { _, isChecked -> + settings.showChatOnMap(chatTitle, isChecked) + if (settings.hasAnyChatToShowOnMap()) { + if (osmandHelper.initialized && !osmandHelper.isOsmandBound()) { + if (isChecked) { + showOsmandMissingDialog() + } + } else { + if (isChecked) { + app.showLocationHelper.showChatMessages(chatTitle) + } else { + app.showLocationHelper.hideChatMessages(chatTitle) + } + app.showLocationHelper.startShowingLocation() + } + } else { + app.showLocationHelper.stopShowingLocation() + if (!isChecked) { + app.showLocationHelper.hideChatMessages(chatTitle) + } + } + } + } + + override fun getItemCount() = chats.size + } } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt index 3cc75dd9eb..81e849a079 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt @@ -12,128 +12,117 @@ import net.osmand.telegram.helpers.ShareLocationHelper import net.osmand.telegram.helpers.ShowLocationHelper import net.osmand.telegram.helpers.TelegramHelper import net.osmand.telegram.notifications.NotificationHelper -import net.osmand.telegram.services.MyLocationService -import net.osmand.telegram.services.UserLocationService import net.osmand.telegram.utils.AndroidUtils class TelegramApplication : Application() { - val telegramHelper = TelegramHelper.instance - lateinit var settings: TelegramSettings private set - lateinit var shareLocationHelper: ShareLocationHelper private set - lateinit var showLocationHelper: ShowLocationHelper private set - lateinit var notificationHelper: NotificationHelper private set - lateinit var osmandHelper: OsmandAidlHelper private set + val telegramHelper = TelegramHelper.instance + lateinit var settings: TelegramSettings private set + lateinit var shareLocationHelper: ShareLocationHelper private set + lateinit var showLocationHelper: ShowLocationHelper private set + lateinit var notificationHelper: NotificationHelper private set + lateinit var osmandHelper: OsmandAidlHelper private set - var myLocationService: MyLocationService? = null - var userLocationService: UserLocationService? = null + var telegramService: TelegramService? = null - private val uiHandler = Handler() + private val uiHandler = Handler() - private val lastTimeInternetConnectionChecked: Long = 0 - private var internetConnectionAvailable = true + private val lastTimeInternetConnectionChecked: Long = 0 + private var internetConnectionAvailable = true - override fun onCreate() { - super.onCreate() - telegramHelper.appDir = filesDir.absolutePath + override fun onCreate() { + super.onCreate() + telegramHelper.appDir = filesDir.absolutePath - settings = TelegramSettings(this) - osmandHelper = OsmandAidlHelper(this) - shareLocationHelper = ShareLocationHelper(this) - showLocationHelper = ShowLocationHelper(this) - notificationHelper = NotificationHelper(this) + settings = TelegramSettings(this) + osmandHelper = OsmandAidlHelper(this) + shareLocationHelper = ShareLocationHelper(this) + showLocationHelper = ShowLocationHelper(this) + notificationHelper = NotificationHelper(this) - if (settings.hasAnyChatToShareLocation() && AndroidUtils.isLocationPermissionAvailable(this)) { - shareLocationHelper.startSharingLocation() - } - if (settings.hasAnyChatToShowOnMap()) { - showLocationHelper.startShowingLocation() - } - } + if (settings.hasAnyChatToShareLocation() && AndroidUtils.isLocationPermissionAvailable(this)) { + shareLocationHelper.startSharingLocation() + } + if (settings.hasAnyChatToShowOnMap()) { + showLocationHelper.startShowingLocation() + } + } - override fun onTerminate() { - super.onTerminate() - // TODO close telegram api in appropriate place - osmandHelper.cleanupResources() - telegramHelper.close() - } + fun cleanupResources() { + osmandHelper.cleanupResources() + telegramHelper.close() + } - val isWifiConnected: Boolean - get() { - val mgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - val ni = mgr.activeNetworkInfo - return ni != null && ni.type == ConnectivityManager.TYPE_WIFI - } + val isWifiConnected: Boolean + get() { + val mgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + val ni = mgr.activeNetworkInfo + return ni != null && ni.type == ConnectivityManager.TYPE_WIFI + } - private val isInternetConnected: Boolean - get() { - val mgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - val active = mgr.activeNetworkInfo - if (active == null) { - return false - } else { - val state = active.state - return state != NetworkInfo.State.DISCONNECTED && state != NetworkInfo.State.DISCONNECTING - } - } + private val isInternetConnected: Boolean + get() { + val mgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + val active = mgr.activeNetworkInfo + if (active == null) { + return false + } else { + val state = active.state + return state != NetworkInfo.State.DISCONNECTED && state != NetworkInfo.State.DISCONNECTING + } + } - // Check internet connection available every 15 seconds - val isInternetConnectionAvailable: Boolean - get() = isInternetConnectionAvailable(false) + // Check internet connection available every 15 seconds + val isInternetConnectionAvailable: Boolean + get() = isInternetConnectionAvailable(false) - fun isInternetConnectionAvailable(update: Boolean): Boolean { - val delta = System.currentTimeMillis() - lastTimeInternetConnectionChecked - if (delta < 0 || delta > 15000 || update) { - internetConnectionAvailable = isInternetConnected - } - return internetConnectionAvailable - } + fun isInternetConnectionAvailable(update: Boolean): Boolean { + val delta = System.currentTimeMillis() - lastTimeInternetConnectionChecked + if (delta < 0 || delta > 15000 || update) { + internetConnectionAvailable = isInternetConnected + } + return internetConnectionAvailable + } - fun startMyLocationService(restart: Boolean = false) { - val serviceIntent = Intent(this, MyLocationService::class.java) + private fun startTelegramService(intent: Int) { + var i = intent + val serviceIntent = Intent(this, TelegramService::class.java) - val myLocationService = myLocationService - if (myLocationService != null && restart) { - myLocationService.stopSelf() - } - if (myLocationService == null || restart) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - startForegroundService(serviceIntent) - } else { - startService(serviceIntent) - } - } - } + val telegramService = telegramService + if (telegramService != null) { + i = intent or telegramService.usedBy + telegramService.stopSelf() + } - fun stopMyLocationService() { - myLocationService?.stopIfNeeded(this) - } + serviceIntent.putExtra(TelegramService.USAGE_INTENT, i) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + startForegroundService(serviceIntent) + } else { + startService(serviceIntent) + } + } - fun startUserLocationService(restart: Boolean = false) { - val serviceIntent = Intent(this, UserLocationService::class.java) + fun startMyLocationService() { + startTelegramService(TelegramService.USED_BY_MY_LOCATION) + } - val userLocationService = userLocationService - if (userLocationService != null && restart) { - userLocationService.stopSelf() - } - if (userLocationService == null || restart) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - startForegroundService(serviceIntent) - } else { - startService(serviceIntent) - } - } - } + fun stopMyLocationService() { + telegramService?.stopIfNeeded(this, TelegramService.USED_BY_MY_LOCATION) + } - fun stopUserLocationService() { - userLocationService?.stopIfNeeded(this) - } + fun startUserLocationService() { + startTelegramService(TelegramService.USED_BY_USERS_LOCATIONS) + } - fun runInUIThread(action: (() -> Unit)) { - uiHandler.post(action) - } + fun stopUserLocationService() { + telegramService?.stopIfNeeded(this, TelegramService.USED_BY_USERS_LOCATIONS) + } - fun runInUIThread(action: (() -> Unit), delay: Long) { - uiHandler.postDelayed(action, delay) - } + fun runInUIThread(action: (() -> Unit)) { + uiHandler.post(action) + } + + fun runInUIThread(action: (() -> Unit), delay: Long) { + uiHandler.postDelayed(action, delay) + } } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt new file mode 100644 index 0000000000..0f0b0937c1 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt @@ -0,0 +1,169 @@ +package net.osmand.telegram + +import android.app.Service +import android.content.Context +import android.content.Intent +import android.location.Location +import android.location.LocationListener +import android.location.LocationManager +import android.os.* +import android.util.Log +import android.widget.Toast +import net.osmand.PlatformUtil +import net.osmand.telegram.helpers.TelegramHelper.TelegramIncomingMessagesListener +import org.drinkless.td.libcore.telegram.TdApi +import java.util.concurrent.Executors + +class TelegramService : Service(), LocationListener, TelegramIncomingMessagesListener { + + private fun app() = application as TelegramApplication + private val binder = LocationServiceBinder() + private val executor = Executors.newSingleThreadExecutor() + + var handler: Handler? = null + var usedBy = 0 + + class LocationServiceBinder : Binder() + + override fun onBind(intent: Intent): IBinder? { + return binder + } + + fun stopIfNeeded(ctx: Context, usageIntent: Int) { + if (usedBy and usageIntent > 0) { + usedBy -= usageIntent + } + if (usedBy == 0) { + val serviceIntent = Intent(ctx, TelegramService::class.java) + ctx.stopService(serviceIntent) + } + } + + override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { + val app = app() + handler = Handler() + usedBy = intent.getIntExtra(USAGE_INTENT, 0) + + app.telegramService = this + app.telegramHelper.incomingMessagesListener = this + + if (needLocation()) { + // request location updates + val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager + try { + locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0f, this@TelegramService) + } catch (e: SecurityException) { + Toast.makeText(this, R.string.no_location_permission, Toast.LENGTH_LONG).show() + Log.d(PlatformUtil.TAG, "Location service permission not granted") + } catch (e: IllegalArgumentException) { + Toast.makeText(this, R.string.gps_not_available, Toast.LENGTH_LONG).show() + Log.d(PlatformUtil.TAG, "GPS location provider not available") + } + } + + val locationNotification = app.notificationHelper.locationNotification + val notification = app.notificationHelper.buildNotification(locationNotification) + startForeground(locationNotification.telegramNotificationId, notification) + app.notificationHelper.refreshNotification(locationNotification.type) + return Service.START_REDELIVER_INTENT + } + + override fun onDestroy() { + super.onDestroy() + val app = app() + app.telegramHelper.incomingMessagesListener = null + app.telegramService = null + + usedBy = 0 + + // remove updates + val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager + try { + locationManager.removeUpdates(this) + } catch (e: SecurityException) { + Log.d(PlatformUtil.TAG, "Location service permission not granted") + } + + // remove notification + stopForeground(java.lang.Boolean.TRUE) + } + + private fun needLocation(): Boolean { + return (usedBy and USED_BY_MY_LOCATION) > 0 + } + + override fun onLocationChanged(l: Location?) { + if (l != null) { + val location = convertLocation(l) + app().shareLocationHelper.updateLocation(location) + } + } + + override fun onProviderDisabled(provider: String) { + Toast.makeText(this, getString(R.string.location_service_no_gps_available), Toast.LENGTH_LONG).show() + } + + + override fun onProviderEnabled(provider: String) {} + + + override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {} + + override fun onTaskRemoved(rootIntent: Intent) { + val app = app() + if (app.telegramService != null) { + // Do not stop service after UI task was dismissed + //this@TelegramService.stopSelf() + } + } + + override fun onReceiveChatLocationMessages(chatTitle: String, vararg messages: TdApi.Message) { + val app = app() + if (app.settings.isShowingChatOnMap(chatTitle)) { + ShowMessagesTask(app, chatTitle).executeOnExecutor(executor, *messages) + } + } + + private class ShowMessagesTask(private val app: TelegramApplication, private val chatTitle: String) : AsyncTask() { + + override fun doInBackground(vararg messages: TdApi.Message): Void? { + for (message in messages) { + app.showLocationHelper.showLocationOnMap(chatTitle, message) + } + return null + } + } + + companion object { + + const val USED_BY_MY_LOCATION: Int = 1 + const val USED_BY_USERS_LOCATIONS: Int = 2 + const val USAGE_INTENT = "SERVICE_USED_BY" + + fun convertLocation(l: Location?): net.osmand.Location? { + if (l == null) { + return null + } + val r = net.osmand.Location(l.provider) + r.latitude = l.latitude + r.longitude = l.longitude + r.time = l.time + if (l.hasAccuracy()) { + r.accuracy = l.accuracy + } + if (l.hasSpeed()) { + r.speed = l.speed + } + if (l.hasAltitude()) { + r.altitude = l.altitude + } + if (l.hasBearing()) { + r.bearing = l.bearing + } + if (l.hasAltitude()) { + r.altitude = l.altitude + } + return r + } + } +} diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt index e3d30dc18b..95205f7d3e 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt @@ -6,128 +6,128 @@ import net.osmand.telegram.utils.OsmandFormatter.SpeedConstants class TelegramSettings(private val app: TelegramApplication) { - companion object { + companion object { - private const val SETTINGS_NAME = "osmand_telegram_settings" + private const val SETTINGS_NAME = "osmand_telegram_settings" - private const val SHARE_LOCATION_CHATS_KEY = "share_location_chats" - private const val SHOW_ON_MAP_CHATS_KEY = "show_on_map_chats" + private const val SHARE_LOCATION_CHATS_KEY = "share_location_chats" + private const val SHOW_ON_MAP_CHATS_KEY = "show_on_map_chats" - private const val METRICS_CONSTANTS_KEY = "metrics_constants" - private const val SPEED_CONSTANTS_KEY = "speed_constants" + private const val METRICS_CONSTANTS_KEY = "metrics_constants" + private const val SPEED_CONSTANTS_KEY = "speed_constants" - private const val SHOW_NOTIFICATION_ALWAYS_KEY = "show_notification_always" - } + private const val SHOW_NOTIFICATION_ALWAYS_KEY = "show_notification_always" + } - private var shareLocationChats: Set = emptySet() - private var showOnMapChats: Set = emptySet() + private var shareLocationChats: Set = emptySet() + private var showOnMapChats: Set = emptySet() - var metricsConstants = MetricsConstants.KILOMETERS_AND_METERS - var speedConstants = SpeedConstants.KILOMETERS_PER_HOUR + var metricsConstants = MetricsConstants.KILOMETERS_AND_METERS + var speedConstants = SpeedConstants.KILOMETERS_PER_HOUR - var showNotificationAlways = true + var showNotificationAlways = true - init { - read() - } + init { + read() + } - fun hasAnyChatToShareLocation(): Boolean { - return shareLocationChats.isNotEmpty() - } + fun hasAnyChatToShareLocation(): Boolean { + return shareLocationChats.isNotEmpty() + } - fun isSharingLocationToChat(chatTitle: String): Boolean { - return shareLocationChats.contains(chatTitle) - } + fun isSharingLocationToChat(chatTitle: String): Boolean { + return shareLocationChats.contains(chatTitle) + } - fun hasAnyChatToShowOnMap(): Boolean { - return showOnMapChats.isNotEmpty() - } + fun hasAnyChatToShowOnMap(): Boolean { + return showOnMapChats.isNotEmpty() + } - fun isShowingChatOnMap(chatTitle: String): Boolean { - return showOnMapChats.contains(chatTitle) - } + fun isShowingChatOnMap(chatTitle: String): Boolean { + return showOnMapChats.contains(chatTitle) + } - fun removeNonexistingChats(presentChatTitles: List) { - val shareLocationChats = shareLocationChats.toMutableList() - shareLocationChats.intersect(presentChatTitles) - this.shareLocationChats = shareLocationChats.toHashSet() + fun removeNonexistingChats(presentChatTitles: List) { + val shareLocationChats = shareLocationChats.toMutableList() + shareLocationChats.intersect(presentChatTitles) + this.shareLocationChats = shareLocationChats.toHashSet() - val showOnMapChats = showOnMapChats.toMutableList() - showOnMapChats.intersect(presentChatTitles) - this.showOnMapChats = showOnMapChats.toHashSet() - } + val showOnMapChats = showOnMapChats.toMutableList() + showOnMapChats.intersect(presentChatTitles) + this.showOnMapChats = showOnMapChats.toHashSet() + } - fun shareLocationToChat(chatTitle: String, share: Boolean) { - val shareLocationChats = shareLocationChats.toMutableList() - if (share) { - shareLocationChats.add(chatTitle) - } else { - shareLocationChats.remove(chatTitle) - } - this.shareLocationChats = shareLocationChats.toHashSet() - } + fun shareLocationToChat(chatTitle: String, share: Boolean) { + val shareLocationChats = shareLocationChats.toMutableList() + if (share) { + shareLocationChats.add(chatTitle) + } else { + shareLocationChats.remove(chatTitle) + } + this.shareLocationChats = shareLocationChats.toHashSet() + } - fun showChatOnMap(chatTitle: String, show: Boolean) { - val showOnMapChats = showOnMapChats.toMutableList() - if (show) { - showOnMapChats.add(chatTitle) - } else { - showOnMapChats.remove(chatTitle) - } - this.showOnMapChats = showOnMapChats.toHashSet() - } + fun showChatOnMap(chatTitle: String, show: Boolean) { + val showOnMapChats = showOnMapChats.toMutableList() + if (show) { + showOnMapChats.add(chatTitle) + } else { + showOnMapChats.remove(chatTitle) + } + this.showOnMapChats = showOnMapChats.toHashSet() + } - fun getShareLocationChats() = ArrayList(shareLocationChats) - fun getShowOnMapChats() = ArrayList(showOnMapChats) + fun getShareLocationChats() = ArrayList(shareLocationChats) + fun getShowOnMapChats() = ArrayList(showOnMapChats) - fun getShowOnMapChatsCount() = showOnMapChats.size + fun getShowOnMapChatsCount() = showOnMapChats.size - fun save() { - val prefs = app.getSharedPreferences(SETTINGS_NAME, Context.MODE_PRIVATE) - val edit = prefs.edit() + fun save() { + val prefs = app.getSharedPreferences(SETTINGS_NAME, Context.MODE_PRIVATE) + val edit = prefs.edit() - val shareLocationChatsSet = mutableSetOf() - val shareLocationChats = ArrayList(shareLocationChats) - for (chatTitle in shareLocationChats) { - shareLocationChatsSet.add(chatTitle) - } - edit.putStringSet(SHARE_LOCATION_CHATS_KEY, shareLocationChatsSet) + val shareLocationChatsSet = mutableSetOf() + val shareLocationChats = ArrayList(shareLocationChats) + for (chatTitle in shareLocationChats) { + shareLocationChatsSet.add(chatTitle) + } + edit.putStringSet(SHARE_LOCATION_CHATS_KEY, shareLocationChatsSet) - val showOnMapChatsSet = mutableSetOf() - val showOnMapChats = ArrayList(showOnMapChats) - for (chatTitle in showOnMapChats) { - showOnMapChatsSet.add(chatTitle) - } - edit.putStringSet(SHOW_ON_MAP_CHATS_KEY, showOnMapChatsSet) + val showOnMapChatsSet = mutableSetOf() + val showOnMapChats = ArrayList(showOnMapChats) + for (chatTitle in showOnMapChats) { + showOnMapChatsSet.add(chatTitle) + } + edit.putStringSet(SHOW_ON_MAP_CHATS_KEY, showOnMapChatsSet) - edit.putString(METRICS_CONSTANTS_KEY, metricsConstants.name) - edit.putString(SPEED_CONSTANTS_KEY, speedConstants.name) + edit.putString(METRICS_CONSTANTS_KEY, metricsConstants.name) + edit.putString(SPEED_CONSTANTS_KEY, speedConstants.name) - edit.putBoolean(SHOW_NOTIFICATION_ALWAYS_KEY, showNotificationAlways) + edit.putBoolean(SHOW_NOTIFICATION_ALWAYS_KEY, showNotificationAlways) - edit.apply() - } + edit.apply() + } - fun read() { - val prefs = app.getSharedPreferences(SETTINGS_NAME, Context.MODE_PRIVATE) + fun read() { + val prefs = app.getSharedPreferences(SETTINGS_NAME, Context.MODE_PRIVATE) - val shareLocationChats = mutableSetOf() - val shareLocationChatsSet = prefs.getStringSet(SHARE_LOCATION_CHATS_KEY, mutableSetOf()) - for (chatTitle in shareLocationChatsSet) { - shareLocationChats.add(chatTitle) - } - this.shareLocationChats = shareLocationChats + val shareLocationChats = mutableSetOf() + val shareLocationChatsSet = prefs.getStringSet(SHARE_LOCATION_CHATS_KEY, mutableSetOf()) + for (chatTitle in shareLocationChatsSet) { + shareLocationChats.add(chatTitle) + } + this.shareLocationChats = shareLocationChats - val showOnMapChats = mutableSetOf() - val showOnMapChatsSet = prefs.getStringSet(SHOW_ON_MAP_CHATS_KEY, mutableSetOf()) - for (chatTitle in showOnMapChatsSet) { - showOnMapChats.add(chatTitle) - } - this.showOnMapChats = showOnMapChats + val showOnMapChats = mutableSetOf() + val showOnMapChatsSet = prefs.getStringSet(SHOW_ON_MAP_CHATS_KEY, mutableSetOf()) + for (chatTitle in showOnMapChatsSet) { + showOnMapChats.add(chatTitle) + } + this.showOnMapChats = showOnMapChats - metricsConstants = MetricsConstants.valueOf(prefs.getString(METRICS_CONSTANTS_KEY, MetricsConstants.KILOMETERS_AND_METERS.name)) - speedConstants = SpeedConstants.valueOf(prefs.getString(SPEED_CONSTANTS_KEY, SpeedConstants.KILOMETERS_PER_HOUR.name)) + metricsConstants = MetricsConstants.valueOf(prefs.getString(METRICS_CONSTANTS_KEY, MetricsConstants.KILOMETERS_AND_METERS.name)) + speedConstants = SpeedConstants.valueOf(prefs.getString(SPEED_CONSTANTS_KEY, SpeedConstants.KILOMETERS_PER_HOUR.name)) - showNotificationAlways = prefs.getBoolean(SHOW_NOTIFICATION_ALWAYS_KEY, true) - } + showNotificationAlways = prefs.getBoolean(SHOW_NOTIFICATION_ALWAYS_KEY, true) + } } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/OsmandAidlHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/OsmandAidlHelper.kt index e1c69c6081..6c0f64cb40 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/OsmandAidlHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/OsmandAidlHelper.kt @@ -48,788 +48,788 @@ import java.util.* class OsmandAidlHelper(private val app: Application) { - companion object { - private const val OSMAND_FREE_PACKAGE_NAME = "net.osmand" - private const val OSMAND_PLUS_PACKAGE_NAME = "net.osmand.plus" - private var OSMAND_PACKAGE_NAME = OSMAND_PLUS_PACKAGE_NAME - } - - private var mIOsmAndAidlInterface: IOsmAndAidlInterface? = null - - var initialized: Boolean = false - private set - - var bound: Boolean = false - private set - - var listener: OsmandHelperListener? = null - - interface OsmandHelperListener { - fun onOsmandConnectionStateChanged(connected: Boolean) - } - - /** - * Class for interacting with the main interface of the service. - */ - private val mConnection = object : ServiceConnection { - override fun onServiceConnected(className: ComponentName, - service: IBinder) { - // This is called when the connection with the service has been - // established, giving us the service object we can use to - // interact with the service. We are communicating with our - // service through an IDL interface, so get a client-side - // representation of that from the raw service object. - mIOsmAndAidlInterface = IOsmAndAidlInterface.Stub.asInterface(service) - initialized = true - Toast.makeText(app, "OsmAnd connected", Toast.LENGTH_SHORT).show() - listener?.onOsmandConnectionStateChanged(true) - } - - override fun onServiceDisconnected(className: ComponentName) { - // This is called when the connection with the service has been - // unexpectedly disconnected -- that is, its process crashed. - mIOsmAndAidlInterface = null - Toast.makeText(app, "OsmAnd disconnected", Toast.LENGTH_SHORT).show() - listener?.onOsmandConnectionStateChanged(false) - } - } - - fun isOsmandBound(): Boolean { - return bound - } - - fun isOsmandConnected(): Boolean { - return mIOsmAndAidlInterface != null - } - - /** - * Get list of active GPX files. - * - * @return list of active gpx files. - */ - val activeGpxFiles: List? - get() { - if (mIOsmAndAidlInterface != null) { - try { - val res = ArrayList() - if (mIOsmAndAidlInterface!!.getActiveGpx(res)) { - return res - } - } catch (e: Throwable) { - e.printStackTrace() - } - - } - return null - } - - init { - when { - bindService(OSMAND_PLUS_PACKAGE_NAME) -> { - OSMAND_PACKAGE_NAME = OSMAND_PLUS_PACKAGE_NAME - bound = true - } - bindService(OSMAND_FREE_PACKAGE_NAME) -> { - OSMAND_PACKAGE_NAME = OSMAND_FREE_PACKAGE_NAME - bound = true - } - else -> { - bound = false - initialized = true - } - } - } - - private fun bindService(packageName: String): Boolean { - return if (mIOsmAndAidlInterface == null) { - val intent = Intent("net.osmand.aidl.OsmandAidlService") - intent.`package` = packageName - app.bindService(intent, mConnection, Context.BIND_AUTO_CREATE) - } else { - true - } - } - - fun cleanupResources() { - if (mIOsmAndAidlInterface != null) { - app.unbindService(mConnection) - } - } - - fun refreshMap(): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - return mIOsmAndAidlInterface!!.refreshMap() - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Add favorite group with given params. - * - * @param name - group name. - * @param color - group color. Can be one of: "red", "orange", "yellow", - * "lightgreen", "green", "lightblue", "blue", "purple", "pink", "brown". - * @param visible - group visibility. - */ - fun addFavoriteGroup(name: String, color: String, visible: Boolean): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - val favoriteGroup = AFavoriteGroup(name, color, visible) - return mIOsmAndAidlInterface!!.addFavoriteGroup(AddFavoriteGroupParams(favoriteGroup)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Update favorite group with given params. - * - * @param namePrev - group name (current). - * @param colorPrev - group color (current). - * @param visiblePrev - group visibility (current). - * @param nameNew - group name (new). - * @param colorNew - group color (new). - * @param visibleNew - group visibility (new). - */ - fun updateFavoriteGroup(namePrev: String, colorPrev: String, visiblePrev: Boolean, - nameNew: String, colorNew: String, visibleNew: Boolean): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - val favoriteGroupPrev = AFavoriteGroup(namePrev, colorPrev, visiblePrev) - val favoriteGroupNew = AFavoriteGroup(nameNew, colorNew, visibleNew) - return mIOsmAndAidlInterface!!.updateFavoriteGroup(UpdateFavoriteGroupParams(favoriteGroupPrev, favoriteGroupNew)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Remove favorite group with given name. - * - * @param name - name of favorite group. - */ - fun removeFavoriteGroup(name: String): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - val favoriteGroup = AFavoriteGroup(name, "", false) - return mIOsmAndAidlInterface!!.removeFavoriteGroup(RemoveFavoriteGroupParams(favoriteGroup)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Add favorite at given location with given params. - * - * @param lat - latitude. - * @param lon - longitude. - * @param name - name of favorite item. - * @param description - description of favorite item. - * @param category - category of favorite item. - * @param color - color of favorite item. Can be one of: "red", "orange", "yellow", - * "lightgreen", "green", "lightblue", "blue", "purple", "pink", "brown". - * @param visible - should favorite item be visible after creation. - */ - fun addFavorite(lat: Double, lon: Double, name: String, description: String, - category: String, color: String, visible: Boolean): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - val favorite = AFavorite(lat, lon, name, description, category, color, visible) - return mIOsmAndAidlInterface!!.addFavorite(AddFavoriteParams(favorite)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Update favorite at given location with given params. - * - * @param latPrev - latitude (current favorite). - * @param lonPrev - longitude (current favorite). - * @param namePrev - name of favorite item (current favorite). - * @param categoryPrev - category of favorite item (current favorite). - * @param latNew - latitude (new favorite). - * @param lonNew - longitude (new favorite). - * @param nameNew - name of favorite item (new favorite). - * @param descriptionNew - description of favorite item (new favorite). - * @param categoryNew - category of favorite item (new favorite). Use only to create a new category, - * not to update an existing one. If you want to update an existing category, - * use the [.updateFavoriteGroup] method. - * @param colorNew - color of new category. Can be one of: "red", "orange", "yellow", - * "lightgreen", "green", "lightblue", "blue", "purple", "pink", "brown". - * @param visibleNew - should new category be visible after creation. - */ - fun updateFavorite(latPrev: Double, lonPrev: Double, namePrev: String, categoryPrev: String, - latNew: Double, lonNew: Double, nameNew: String, descriptionNew: String, - categoryNew: String, colorNew: String, visibleNew: Boolean): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - val favoritePrev = AFavorite(latPrev, lonPrev, namePrev, "", categoryPrev, "", false) - val favoriteNew = AFavorite(latNew, lonNew, nameNew, descriptionNew, categoryNew, colorNew, visibleNew) - return mIOsmAndAidlInterface!!.updateFavorite(UpdateFavoriteParams(favoritePrev, favoriteNew)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Remove favorite at given location with given params. - * - * @param lat - latitude. - * @param lon - longitude. - * @param name - name of favorite item. - * @param category - category of favorite item. - */ - fun removeFavorite(lat: Double, lon: Double, name: String, category: String): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - val favorite = AFavorite(lat, lon, name, "", category, "", false) - return mIOsmAndAidlInterface!!.removeFavorite(RemoveFavoriteParams(favorite)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Add map marker at given location. - * - * @param lat - latitude. - * @param lon - longitude. - * @param name - name. - */ - fun addMapMarker(lat: Double, lon: Double, name: String): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - val marker = AMapMarker(ALatLon(lat, lon), name) - return mIOsmAndAidlInterface!!.addMapMarker(AddMapMarkerParams(marker)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Update map marker at given location with name. - * - * @param latPrev - latitude (current marker). - * @param lonPrev - longitude (current marker). - * @param namePrev - name (current marker). - * @param latNew - latitude (new marker). - * @param lonNew - longitude (new marker). - * @param nameNew - name (new marker). - */ - fun updateMapMarker(latPrev: Double, lonPrev: Double, namePrev: String, - latNew: Double, lonNew: Double, nameNew: String): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - val markerPrev = AMapMarker(ALatLon(latPrev, lonPrev), namePrev) - val markerNew = AMapMarker(ALatLon(latNew, lonNew), nameNew) - return mIOsmAndAidlInterface!!.updateMapMarker(UpdateMapMarkerParams(markerPrev, markerNew)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Remove map marker at given location with name. - * - * @param lat - latitude. - * @param lon - longitude. - * @param name - name. - */ - fun removeMapMarker(lat: Double, lon: Double, name: String): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - val marker = AMapMarker(ALatLon(lat, lon), name) - return mIOsmAndAidlInterface!!.removeMapMarker(RemoveMapMarkerParams(marker)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Add map widget to the right side of the main screen. - * Note: any specified icon should exist in OsmAnd app resources. - * - * @param id - widget id. - * @param menuIconName - icon name (configure map menu). - * @param menuTitle - widget name (configure map menu). - * @param lightIconName - icon name for the light theme (widget). - * @param darkIconName - icon name for the dark theme (widget). - * @param text - main widget text. - * @param description - sub text, like "km/h". - * @param order - order position in the widgets list. - * @param intentOnClick - onClick intent. Called after click on widget as startActivity(Intent intent). - */ - fun addMapWidget(id: String, menuIconName: String, menuTitle: String, - lightIconName: String, darkIconName: String, text: String, description: String, - order: Int, intentOnClick: Intent): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - val widget = AMapWidget(id, menuIconName, menuTitle, lightIconName, - darkIconName, text, description, order, intentOnClick) - return mIOsmAndAidlInterface!!.addMapWidget(AddMapWidgetParams(widget)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Update map widget. - * Note: any specified icon should exist in OsmAnd app resources. - * - * @param id - widget id. - * @param menuIconName - icon name (configure map menu). - * @param menuTitle - widget name (configure map menu). - * @param lightIconName - icon name for the light theme (widget). - * @param darkIconName - icon name for the dark theme (widget). - * @param text - main widget text. - * @param description - sub text, like "km/h". - * @param order - order position in the widgets list. - * @param intentOnClick - onClick intent. Called after click on widget as startActivity(Intent intent). - */ - fun updateMapWidget(id: String, menuIconName: String, menuTitle: String, - lightIconName: String, darkIconName: String, text: String, description: String, - order: Int, intentOnClick: Intent): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - val widget = AMapWidget(id, menuIconName, menuTitle, lightIconName, - darkIconName, text, description, order, intentOnClick) - return mIOsmAndAidlInterface!!.updateMapWidget(UpdateMapWidgetParams(widget)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Remove map widget. - * - * @param id - widget id. - */ - fun removeMapWidget(id: String): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - return mIOsmAndAidlInterface!!.removeMapWidget(RemoveMapWidgetParams(id)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Add user layer on the map. - * - * @param id - layer id. - * @param name - layer name. - * @param zOrder - z-order position of layer. Default value is 5.5f - * @param points - initial list of points. Nullable. - */ - fun addMapLayer(id: String, name: String, zOrder: Float, points: List?): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - val layer = AMapLayer(id, name, zOrder, points) - return mIOsmAndAidlInterface!!.addMapLayer(AddMapLayerParams(layer)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Update user layer. - * - * @param id - layer id. - * @param name - layer name. - * @param zOrder - z-order position of layer. Default value is 5.5f - * @param points - list of points. Nullable. - */ - fun updateMapLayer(id: String, name: String, zOrder: Float, points: List?): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - val layer = AMapLayer(id, name, zOrder, points) - return mIOsmAndAidlInterface!!.updateMapLayer(UpdateMapLayerParams(layer)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Remove user layer. - * - * @param id - layer id. - */ - fun removeMapLayer(id: String): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - return mIOsmAndAidlInterface!!.removeMapLayer(RemoveMapLayerParams(id)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Add point to user layer. - * - * @param layerId - layer id. Note: layer should be added first. - * @param pointId - point id. - * @param shortName - short name (single char). Displayed on the map. - * @param fullName - full name. Displayed in the context menu on first row. - * @param typeName - type name. Displayed in context menu on second row. - * @param color - color of circle's background. - * @param location - location of the point. - * @param details - list of details. Displayed under context menu. - */ - fun addMapPoint(layerId: String, pointId: String, shortName: String, fullName: String, - typeName: String, color: Int, location: ALatLon, details: List?): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - val point = AMapPoint(pointId, shortName, fullName, typeName, color, location, details) - return mIOsmAndAidlInterface!!.addMapPoint(AddMapPointParams(layerId, point)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Update point. - * - * @param layerId - layer id. - * @param pointId - point id. - * @param shortName - short name (single char). Displayed on the map. - * @param fullName - full name. Displayed in the context menu on first row. - * @param typeName - type name. Displayed in context menu on second row. - * @param color - color of circle's background. - * @param location - location of the point. - * @param details - list of details. Displayed under context menu. - */ - fun updateMapPoint(layerId: String, pointId: String, shortName: String, fullName: String, - typeName: String, color: Int, location: ALatLon, details: List?): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - val point = AMapPoint(pointId, shortName, fullName, typeName, color, location, details) - return mIOsmAndAidlInterface!!.updateMapPoint(UpdateMapPointParams(layerId, point)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Remove point. - * - * @param layerId - layer id. - * @param pointId - point id. - */ - fun removeMapPoint(layerId: String, pointId: String): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - return mIOsmAndAidlInterface!!.removeMapPoint(RemoveMapPointParams(layerId, pointId)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Import GPX file to OsmAnd. - * OsmAnd must have rights to access location. Not recommended. - * - * @param file - File which represents GPX track. - * @param fileName - Destination file name. May contain dirs. - * @param color - color of gpx. Can be one of: "red", "orange", "lightblue", "blue", "purple", - * "translucent_red", "translucent_orange", "translucent_lightblue", - * "translucent_blue", "translucent_purple" - * @param show - show track on the map after import - */ - fun importGpxFromFile(file: File, fileName: String, color: String, show: Boolean): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - return mIOsmAndAidlInterface!!.importGpx(ImportGpxParams(file, fileName, color, show)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Import GPX file to OsmAnd. - * - * @param gpxUri - URI created by FileProvider. - * @param fileName - Destination file name. May contain dirs. - * @param color - color of gpx. Can be one of: "", "red", "orange", "lightblue", "blue", "purple", - * "translucent_red", "translucent_orange", "translucent_lightblue", - * "translucent_blue", "translucent_purple" - * @param show - show track on the map after import - */ - fun importGpxFromUri(gpxUri: Uri, fileName: String, color: String, show: Boolean): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - app.grantUriPermission(OSMAND_PACKAGE_NAME, gpxUri, Intent.FLAG_GRANT_READ_URI_PERMISSION) - return mIOsmAndAidlInterface!!.importGpx(ImportGpxParams(gpxUri, fileName, color, show)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - fun navigateGpxFromUri(gpxUri: Uri, force: Boolean): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - app.grantUriPermission(OSMAND_PACKAGE_NAME, gpxUri, Intent.FLAG_GRANT_READ_URI_PERMISSION) - return mIOsmAndAidlInterface!!.navigateGpx(NavigateGpxParams(gpxUri, force)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Import GPX file to OsmAnd. - * - * @param data - Raw contents of GPX file. Sent as intent's extra string parameter. - * @param fileName - Destination file name. May contain dirs. - * @param color - color of gpx. Can be one of: "red", "orange", "lightblue", "blue", "purple", - * "translucent_red", "translucent_orange", "translucent_lightblue", - * "translucent_blue", "translucent_purple" - * @param show - show track on the map after import - */ - fun importGpxFromData(data: String, fileName: String, color: String, show: Boolean): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - return mIOsmAndAidlInterface!!.importGpx(ImportGpxParams(data, fileName, color, show)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - fun navigateGpxFromData(data: String, force: Boolean): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - return mIOsmAndAidlInterface!!.navigateGpx(NavigateGpxParams(data, force)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Show GPX file on map. - * - * @param fileName - file name to show. Must be imported first. - */ - fun showGpx(fileName: String): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - return mIOsmAndAidlInterface!!.showGpx(ShowGpxParams(fileName)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Hide GPX file. - * - * @param fileName - file name to hide. - */ - fun hideGpx(fileName: String): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - return mIOsmAndAidlInterface!!.hideGpx(HideGpxParams(fileName)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Remove GPX file. - * - * @param fileName - file name to remove; - */ - fun removeGpx(fileName: String): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - return mIOsmAndAidlInterface!!.removeGpx(RemoveGpxParams(fileName)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - /** - * Get list of active GPX files. - * - * @param latitude - latitude of new map center. - * @param longitude - longitude of new map center. - * @param zoom - map zoom level. Set 0 to keep zoom unchanged. - * @param animated - set true to animate changes. - */ - fun setMapLocation(latitude: Double, longitude: Double, zoom: Int, animated: Boolean): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - return mIOsmAndAidlInterface!!.setMapLocation( - SetMapLocationParams(latitude, longitude, zoom, animated)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - fun startGpxRecording(params: StartGpxRecordingParams): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - return mIOsmAndAidlInterface!!.startGpxRecording(params) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - fun stopGpxRecording(params: StopGpxRecordingParams): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - return mIOsmAndAidlInterface!!.stopGpxRecording(params) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - fun takePhotoNote(lat: Double, lon: Double): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - return mIOsmAndAidlInterface!!.takePhotoNote(TakePhotoNoteParams(lat, lon)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - fun startVideoRecording(lat: Double, lon: Double): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - return mIOsmAndAidlInterface!!.startVideoRecording(StartVideoRecordingParams(lat, lon)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - fun startAudioRecording(lat: Double, lon: Double): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - return mIOsmAndAidlInterface!!.startAudioRecording(StartAudioRecordingParams(lat, lon)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - fun stopRecording(): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - return mIOsmAndAidlInterface!!.stopRecording(StopRecordingParams()) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } - - fun navigate(startName: String, startLat: Double, startLon: Double, destName: String, destLat: Double, destLon: Double, profile: String, force: Boolean): Boolean { - if (mIOsmAndAidlInterface != null) { - try { - return mIOsmAndAidlInterface!!.navigate(NavigateParams(startName, startLat, startLon, destName, destLat, destLon, profile, force)) - } catch (e: RemoteException) { - e.printStackTrace() - } - - } - return false - } + companion object { + private const val OSMAND_FREE_PACKAGE_NAME = "net.osmand" + private const val OSMAND_PLUS_PACKAGE_NAME = "net.osmand.plus" + private var OSMAND_PACKAGE_NAME = OSMAND_PLUS_PACKAGE_NAME + } + + private var mIOsmAndAidlInterface: IOsmAndAidlInterface? = null + + var initialized: Boolean = false + private set + + var bound: Boolean = false + private set + + var listener: OsmandHelperListener? = null + + interface OsmandHelperListener { + fun onOsmandConnectionStateChanged(connected: Boolean) + } + + /** + * Class for interacting with the main interface of the service. + */ + private val mConnection = object : ServiceConnection { + override fun onServiceConnected(className: ComponentName, + service: IBinder) { + // This is called when the connection with the service has been + // established, giving us the service object we can use to + // interact with the service. We are communicating with our + // service through an IDL interface, so get a client-side + // representation of that from the raw service object. + mIOsmAndAidlInterface = IOsmAndAidlInterface.Stub.asInterface(service) + initialized = true + Toast.makeText(app, "OsmAnd connected", Toast.LENGTH_SHORT).show() + listener?.onOsmandConnectionStateChanged(true) + } + + override fun onServiceDisconnected(className: ComponentName) { + // This is called when the connection with the service has been + // unexpectedly disconnected -- that is, its process crashed. + mIOsmAndAidlInterface = null + Toast.makeText(app, "OsmAnd disconnected", Toast.LENGTH_SHORT).show() + listener?.onOsmandConnectionStateChanged(false) + } + } + + fun isOsmandBound(): Boolean { + return bound + } + + fun isOsmandConnected(): Boolean { + return mIOsmAndAidlInterface != null + } + + /** + * Get list of active GPX files. + * + * @return list of active gpx files. + */ + val activeGpxFiles: List? + get() { + if (mIOsmAndAidlInterface != null) { + try { + val res = ArrayList() + if (mIOsmAndAidlInterface!!.getActiveGpx(res)) { + return res + } + } catch (e: Throwable) { + e.printStackTrace() + } + + } + return null + } + + init { + when { + bindService(OSMAND_PLUS_PACKAGE_NAME) -> { + OSMAND_PACKAGE_NAME = OSMAND_PLUS_PACKAGE_NAME + bound = true + } + bindService(OSMAND_FREE_PACKAGE_NAME) -> { + OSMAND_PACKAGE_NAME = OSMAND_FREE_PACKAGE_NAME + bound = true + } + else -> { + bound = false + initialized = true + } + } + } + + private fun bindService(packageName: String): Boolean { + return if (mIOsmAndAidlInterface == null) { + val intent = Intent("net.osmand.aidl.OsmandAidlService") + intent.`package` = packageName + app.bindService(intent, mConnection, Context.BIND_AUTO_CREATE) + } else { + true + } + } + + fun cleanupResources() { + if (mIOsmAndAidlInterface != null) { + app.unbindService(mConnection) + } + } + + fun refreshMap(): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + return mIOsmAndAidlInterface!!.refreshMap() + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Add favorite group with given params. + * + * @param name - group name. + * @param color - group color. Can be one of: "red", "orange", "yellow", + * "lightgreen", "green", "lightblue", "blue", "purple", "pink", "brown". + * @param visible - group visibility. + */ + fun addFavoriteGroup(name: String, color: String, visible: Boolean): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + val favoriteGroup = AFavoriteGroup(name, color, visible) + return mIOsmAndAidlInterface!!.addFavoriteGroup(AddFavoriteGroupParams(favoriteGroup)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Update favorite group with given params. + * + * @param namePrev - group name (current). + * @param colorPrev - group color (current). + * @param visiblePrev - group visibility (current). + * @param nameNew - group name (new). + * @param colorNew - group color (new). + * @param visibleNew - group visibility (new). + */ + fun updateFavoriteGroup(namePrev: String, colorPrev: String, visiblePrev: Boolean, + nameNew: String, colorNew: String, visibleNew: Boolean): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + val favoriteGroupPrev = AFavoriteGroup(namePrev, colorPrev, visiblePrev) + val favoriteGroupNew = AFavoriteGroup(nameNew, colorNew, visibleNew) + return mIOsmAndAidlInterface!!.updateFavoriteGroup(UpdateFavoriteGroupParams(favoriteGroupPrev, favoriteGroupNew)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Remove favorite group with given name. + * + * @param name - name of favorite group. + */ + fun removeFavoriteGroup(name: String): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + val favoriteGroup = AFavoriteGroup(name, "", false) + return mIOsmAndAidlInterface!!.removeFavoriteGroup(RemoveFavoriteGroupParams(favoriteGroup)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Add favorite at given location with given params. + * + * @param lat - latitude. + * @param lon - longitude. + * @param name - name of favorite item. + * @param description - description of favorite item. + * @param category - category of favorite item. + * @param color - color of favorite item. Can be one of: "red", "orange", "yellow", + * "lightgreen", "green", "lightblue", "blue", "purple", "pink", "brown". + * @param visible - should favorite item be visible after creation. + */ + fun addFavorite(lat: Double, lon: Double, name: String, description: String, + category: String, color: String, visible: Boolean): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + val favorite = AFavorite(lat, lon, name, description, category, color, visible) + return mIOsmAndAidlInterface!!.addFavorite(AddFavoriteParams(favorite)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Update favorite at given location with given params. + * + * @param latPrev - latitude (current favorite). + * @param lonPrev - longitude (current favorite). + * @param namePrev - name of favorite item (current favorite). + * @param categoryPrev - category of favorite item (current favorite). + * @param latNew - latitude (new favorite). + * @param lonNew - longitude (new favorite). + * @param nameNew - name of favorite item (new favorite). + * @param descriptionNew - description of favorite item (new favorite). + * @param categoryNew - category of favorite item (new favorite). Use only to create a new category, + * not to update an existing one. If you want to update an existing category, + * use the [.updateFavoriteGroup] method. + * @param colorNew - color of new category. Can be one of: "red", "orange", "yellow", + * "lightgreen", "green", "lightblue", "blue", "purple", "pink", "brown". + * @param visibleNew - should new category be visible after creation. + */ + fun updateFavorite(latPrev: Double, lonPrev: Double, namePrev: String, categoryPrev: String, + latNew: Double, lonNew: Double, nameNew: String, descriptionNew: String, + categoryNew: String, colorNew: String, visibleNew: Boolean): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + val favoritePrev = AFavorite(latPrev, lonPrev, namePrev, "", categoryPrev, "", false) + val favoriteNew = AFavorite(latNew, lonNew, nameNew, descriptionNew, categoryNew, colorNew, visibleNew) + return mIOsmAndAidlInterface!!.updateFavorite(UpdateFavoriteParams(favoritePrev, favoriteNew)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Remove favorite at given location with given params. + * + * @param lat - latitude. + * @param lon - longitude. + * @param name - name of favorite item. + * @param category - category of favorite item. + */ + fun removeFavorite(lat: Double, lon: Double, name: String, category: String): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + val favorite = AFavorite(lat, lon, name, "", category, "", false) + return mIOsmAndAidlInterface!!.removeFavorite(RemoveFavoriteParams(favorite)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Add map marker at given location. + * + * @param lat - latitude. + * @param lon - longitude. + * @param name - name. + */ + fun addMapMarker(lat: Double, lon: Double, name: String): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + val marker = AMapMarker(ALatLon(lat, lon), name) + return mIOsmAndAidlInterface!!.addMapMarker(AddMapMarkerParams(marker)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Update map marker at given location with name. + * + * @param latPrev - latitude (current marker). + * @param lonPrev - longitude (current marker). + * @param namePrev - name (current marker). + * @param latNew - latitude (new marker). + * @param lonNew - longitude (new marker). + * @param nameNew - name (new marker). + */ + fun updateMapMarker(latPrev: Double, lonPrev: Double, namePrev: String, + latNew: Double, lonNew: Double, nameNew: String): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + val markerPrev = AMapMarker(ALatLon(latPrev, lonPrev), namePrev) + val markerNew = AMapMarker(ALatLon(latNew, lonNew), nameNew) + return mIOsmAndAidlInterface!!.updateMapMarker(UpdateMapMarkerParams(markerPrev, markerNew)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Remove map marker at given location with name. + * + * @param lat - latitude. + * @param lon - longitude. + * @param name - name. + */ + fun removeMapMarker(lat: Double, lon: Double, name: String): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + val marker = AMapMarker(ALatLon(lat, lon), name) + return mIOsmAndAidlInterface!!.removeMapMarker(RemoveMapMarkerParams(marker)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Add map widget to the right side of the main screen. + * Note: any specified icon should exist in OsmAnd app resources. + * + * @param id - widget id. + * @param menuIconName - icon name (configure map menu). + * @param menuTitle - widget name (configure map menu). + * @param lightIconName - icon name for the light theme (widget). + * @param darkIconName - icon name for the dark theme (widget). + * @param text - main widget text. + * @param description - sub text, like "km/h". + * @param order - order position in the widgets list. + * @param intentOnClick - onClick intent. Called after click on widget as startActivity(Intent intent). + */ + fun addMapWidget(id: String, menuIconName: String, menuTitle: String, + lightIconName: String, darkIconName: String, text: String, description: String, + order: Int, intentOnClick: Intent): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + val widget = AMapWidget(id, menuIconName, menuTitle, lightIconName, + darkIconName, text, description, order, intentOnClick) + return mIOsmAndAidlInterface!!.addMapWidget(AddMapWidgetParams(widget)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Update map widget. + * Note: any specified icon should exist in OsmAnd app resources. + * + * @param id - widget id. + * @param menuIconName - icon name (configure map menu). + * @param menuTitle - widget name (configure map menu). + * @param lightIconName - icon name for the light theme (widget). + * @param darkIconName - icon name for the dark theme (widget). + * @param text - main widget text. + * @param description - sub text, like "km/h". + * @param order - order position in the widgets list. + * @param intentOnClick - onClick intent. Called after click on widget as startActivity(Intent intent). + */ + fun updateMapWidget(id: String, menuIconName: String, menuTitle: String, + lightIconName: String, darkIconName: String, text: String, description: String, + order: Int, intentOnClick: Intent): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + val widget = AMapWidget(id, menuIconName, menuTitle, lightIconName, + darkIconName, text, description, order, intentOnClick) + return mIOsmAndAidlInterface!!.updateMapWidget(UpdateMapWidgetParams(widget)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Remove map widget. + * + * @param id - widget id. + */ + fun removeMapWidget(id: String): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + return mIOsmAndAidlInterface!!.removeMapWidget(RemoveMapWidgetParams(id)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Add user layer on the map. + * + * @param id - layer id. + * @param name - layer name. + * @param zOrder - z-order position of layer. Default value is 5.5f + * @param points - initial list of points. Nullable. + */ + fun addMapLayer(id: String, name: String, zOrder: Float, points: List?): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + val layer = AMapLayer(id, name, zOrder, points) + return mIOsmAndAidlInterface!!.addMapLayer(AddMapLayerParams(layer)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Update user layer. + * + * @param id - layer id. + * @param name - layer name. + * @param zOrder - z-order position of layer. Default value is 5.5f + * @param points - list of points. Nullable. + */ + fun updateMapLayer(id: String, name: String, zOrder: Float, points: List?): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + val layer = AMapLayer(id, name, zOrder, points) + return mIOsmAndAidlInterface!!.updateMapLayer(UpdateMapLayerParams(layer)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Remove user layer. + * + * @param id - layer id. + */ + fun removeMapLayer(id: String): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + return mIOsmAndAidlInterface!!.removeMapLayer(RemoveMapLayerParams(id)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Add point to user layer. + * + * @param layerId - layer id. Note: layer should be added first. + * @param pointId - point id. + * @param shortName - short name (single char). Displayed on the map. + * @param fullName - full name. Displayed in the context menu on first row. + * @param typeName - type name. Displayed in context menu on second row. + * @param color - color of circle's background. + * @param location - location of the point. + * @param details - list of details. Displayed under context menu. + */ + fun addMapPoint(layerId: String, pointId: String, shortName: String, fullName: String, + typeName: String, color: Int, location: ALatLon, details: List?): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + val point = AMapPoint(pointId, shortName, fullName, typeName, color, location, details) + return mIOsmAndAidlInterface!!.addMapPoint(AddMapPointParams(layerId, point)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Update point. + * + * @param layerId - layer id. + * @param pointId - point id. + * @param shortName - short name (single char). Displayed on the map. + * @param fullName - full name. Displayed in the context menu on first row. + * @param typeName - type name. Displayed in context menu on second row. + * @param color - color of circle's background. + * @param location - location of the point. + * @param details - list of details. Displayed under context menu. + */ + fun updateMapPoint(layerId: String, pointId: String, shortName: String, fullName: String, + typeName: String, color: Int, location: ALatLon, details: List?): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + val point = AMapPoint(pointId, shortName, fullName, typeName, color, location, details) + return mIOsmAndAidlInterface!!.updateMapPoint(UpdateMapPointParams(layerId, point)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Remove point. + * + * @param layerId - layer id. + * @param pointId - point id. + */ + fun removeMapPoint(layerId: String, pointId: String): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + return mIOsmAndAidlInterface!!.removeMapPoint(RemoveMapPointParams(layerId, pointId)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Import GPX file to OsmAnd. + * OsmAnd must have rights to access location. Not recommended. + * + * @param file - File which represents GPX track. + * @param fileName - Destination file name. May contain dirs. + * @param color - color of gpx. Can be one of: "red", "orange", "lightblue", "blue", "purple", + * "translucent_red", "translucent_orange", "translucent_lightblue", + * "translucent_blue", "translucent_purple" + * @param show - show track on the map after import + */ + fun importGpxFromFile(file: File, fileName: String, color: String, show: Boolean): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + return mIOsmAndAidlInterface!!.importGpx(ImportGpxParams(file, fileName, color, show)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Import GPX file to OsmAnd. + * + * @param gpxUri - URI created by FileProvider. + * @param fileName - Destination file name. May contain dirs. + * @param color - color of gpx. Can be one of: "", "red", "orange", "lightblue", "blue", "purple", + * "translucent_red", "translucent_orange", "translucent_lightblue", + * "translucent_blue", "translucent_purple" + * @param show - show track on the map after import + */ + fun importGpxFromUri(gpxUri: Uri, fileName: String, color: String, show: Boolean): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + app.grantUriPermission(OSMAND_PACKAGE_NAME, gpxUri, Intent.FLAG_GRANT_READ_URI_PERMISSION) + return mIOsmAndAidlInterface!!.importGpx(ImportGpxParams(gpxUri, fileName, color, show)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + fun navigateGpxFromUri(gpxUri: Uri, force: Boolean): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + app.grantUriPermission(OSMAND_PACKAGE_NAME, gpxUri, Intent.FLAG_GRANT_READ_URI_PERMISSION) + return mIOsmAndAidlInterface!!.navigateGpx(NavigateGpxParams(gpxUri, force)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Import GPX file to OsmAnd. + * + * @param data - Raw contents of GPX file. Sent as intent's extra string parameter. + * @param fileName - Destination file name. May contain dirs. + * @param color - color of gpx. Can be one of: "red", "orange", "lightblue", "blue", "purple", + * "translucent_red", "translucent_orange", "translucent_lightblue", + * "translucent_blue", "translucent_purple" + * @param show - show track on the map after import + */ + fun importGpxFromData(data: String, fileName: String, color: String, show: Boolean): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + return mIOsmAndAidlInterface!!.importGpx(ImportGpxParams(data, fileName, color, show)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + fun navigateGpxFromData(data: String, force: Boolean): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + return mIOsmAndAidlInterface!!.navigateGpx(NavigateGpxParams(data, force)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Show GPX file on map. + * + * @param fileName - file name to show. Must be imported first. + */ + fun showGpx(fileName: String): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + return mIOsmAndAidlInterface!!.showGpx(ShowGpxParams(fileName)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Hide GPX file. + * + * @param fileName - file name to hide. + */ + fun hideGpx(fileName: String): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + return mIOsmAndAidlInterface!!.hideGpx(HideGpxParams(fileName)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Remove GPX file. + * + * @param fileName - file name to remove; + */ + fun removeGpx(fileName: String): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + return mIOsmAndAidlInterface!!.removeGpx(RemoveGpxParams(fileName)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + /** + * Get list of active GPX files. + * + * @param latitude - latitude of new map center. + * @param longitude - longitude of new map center. + * @param zoom - map zoom level. Set 0 to keep zoom unchanged. + * @param animated - set true to animate changes. + */ + fun setMapLocation(latitude: Double, longitude: Double, zoom: Int, animated: Boolean): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + return mIOsmAndAidlInterface!!.setMapLocation( + SetMapLocationParams(latitude, longitude, zoom, animated)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + fun startGpxRecording(params: StartGpxRecordingParams): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + return mIOsmAndAidlInterface!!.startGpxRecording(params) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + fun stopGpxRecording(params: StopGpxRecordingParams): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + return mIOsmAndAidlInterface!!.stopGpxRecording(params) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + fun takePhotoNote(lat: Double, lon: Double): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + return mIOsmAndAidlInterface!!.takePhotoNote(TakePhotoNoteParams(lat, lon)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + fun startVideoRecording(lat: Double, lon: Double): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + return mIOsmAndAidlInterface!!.startVideoRecording(StartVideoRecordingParams(lat, lon)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + fun startAudioRecording(lat: Double, lon: Double): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + return mIOsmAndAidlInterface!!.startAudioRecording(StartAudioRecordingParams(lat, lon)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + fun stopRecording(): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + return mIOsmAndAidlInterface!!.stopRecording(StopRecordingParams()) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } + + fun navigate(startName: String, startLat: Double, startLon: Double, destName: String, destLat: Double, destLon: Double, profile: String, force: Boolean): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + return mIOsmAndAidlInterface!!.navigate(NavigateParams(startName, startLat, startLon, destName, destLat, destLon, profile, force)) + } catch (e: RemoteException) { + e.printStackTrace() + } + + } + return false + } } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShareLocationHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShareLocationHelper.kt index f8cbfdc863..09f3bc5499 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShareLocationHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShareLocationHelper.kt @@ -6,88 +6,88 @@ import net.osmand.telegram.notifications.TelegramNotification.NotificationType class ShareLocationHelper(private val app: TelegramApplication) { - companion object { - const val MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC = 60 * 60 * 24 - 1 // day - } + companion object { + const val MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC = 60 * 60 * 24 - 1 // day + } - var sharingLocation: Boolean = false - private set + var sharingLocation: Boolean = false + private set - var duration: Long = 0 - private set + var duration: Long = 0 + private set - var distance: Int = 0 - private set + var distance: Int = 0 + private set - var lastLocationMessageSentTime: Long = 0 + var lastLocationMessageSentTime: Long = 0 - private var lastTimeInMillis: Long = 0L + private var lastTimeInMillis: Long = 0L - private var lastLocation: Location? = null - set(value) { - if (lastTimeInMillis == 0L) { - lastTimeInMillis = System.currentTimeMillis() - } else { - val currentTimeInMillis = System.currentTimeMillis() - duration += currentTimeInMillis - lastTimeInMillis - lastTimeInMillis = currentTimeInMillis - } - if (lastLocation != null && value != null) { - distance += value.distanceTo(lastLocation).toInt() - } - field = value - } + private var lastLocation: Location? = null + set(value) { + if (lastTimeInMillis == 0L) { + lastTimeInMillis = System.currentTimeMillis() + } else { + val currentTimeInMillis = System.currentTimeMillis() + duration += currentTimeInMillis - lastTimeInMillis + lastTimeInMillis = currentTimeInMillis + } + if (lastLocation != null && value != null) { + distance += value.distanceTo(lastLocation).toInt() + } + field = value + } - fun updateLocation(location: Location?) { - lastLocation = location + fun updateLocation(location: Location?) { + lastLocation = location - if (location != null && app.isInternetConnectionAvailable) { - val shareLocationChats = app.settings.getShareLocationChats() - if (shareLocationChats.isNotEmpty()) { - app.telegramHelper.sendLiveLocationMessage(shareLocationChats, MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC, location.latitude, location.longitude) - } - lastLocationMessageSentTime = System.currentTimeMillis() - } - refreshNotification() - } + if (location != null && app.isInternetConnectionAvailable) { + val shareLocationChats = app.settings.getShareLocationChats() + if (shareLocationChats.isNotEmpty()) { + app.telegramHelper.sendLiveLocationMessage(shareLocationChats, MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC, location.latitude, location.longitude) + } + lastLocationMessageSentTime = System.currentTimeMillis() + } + refreshNotification() + } - fun startSharingLocation() { - if (!sharingLocation) { - sharingLocation = true + fun startSharingLocation() { + if (!sharingLocation) { + sharingLocation = true - app.startMyLocationService() + app.startMyLocationService() - refreshNotification() - } - } + refreshNotification() + } + } - fun stopSharingLocation() { - if (sharingLocation) { - sharingLocation = false + fun stopSharingLocation() { + if (sharingLocation) { + sharingLocation = false - app.stopMyLocationService() - lastLocation = null - lastTimeInMillis = 0L - distance = 0 - duration = 0 + app.stopMyLocationService() + lastLocation = null + lastTimeInMillis = 0L + distance = 0 + duration = 0 - refreshNotification() - } - } + refreshNotification() + } + } - fun pauseSharingLocation() { - sharingLocation = false + fun pauseSharingLocation() { + sharingLocation = false - app.stopMyLocationService() - lastLocation = null - lastTimeInMillis = 0L + app.stopMyLocationService() + lastLocation = null + lastTimeInMillis = 0L - refreshNotification() - } + refreshNotification() + } - private fun refreshNotification() { - app.runInUIThread { - app.notificationHelper.refreshNotification(NotificationType.SHARE_LOCATION) - } - } + private fun refreshNotification() { + app.runInUIThread { + app.notificationHelper.refreshNotification(NotificationType.LOCATION) + } + } } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt index ba6dc98e19..091e31ff4e 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/ShowLocationHelper.kt @@ -7,77 +7,77 @@ import org.drinkless.td.libcore.telegram.TdApi class ShowLocationHelper(private val app: TelegramApplication) { - companion object { - private const val MAP_LAYER_ID = "telegram_layer" - } + companion object { + private const val MAP_LAYER_ID = "telegram_layer" + } - private val telegramHelper = app.telegramHelper - private val osmandHelper = app.osmandHelper + private val telegramHelper = app.telegramHelper + private val osmandHelper = app.osmandHelper - var showingLocation: Boolean = false - private set + var showingLocation: Boolean = false + private set - private fun setMapLayer() { - osmandHelper.addMapLayer(MAP_LAYER_ID, "Telegram", 5.5f, null) - } + private fun setMapLayer() { + osmandHelper.addMapLayer(MAP_LAYER_ID, "Telegram", 5.5f, null) + } - fun showLocationOnMap(chatTitle: String, message: TdApi.Message) { - if (osmandHelper.isOsmandConnected()) { - val content = message.content - if (content is TdApi.MessageLocation) { - var userName = "" - val user = telegramHelper.getUser(message.senderUserId) - if (user != null) { - userName = "${user.firstName} ${user.lastName}".trim() - if (userName.isEmpty()) { - userName = user.username - } - if (userName.isEmpty()) { - userName = user.phoneNumber - } - } - if (userName.isEmpty()) { - userName = message.senderUserId.toString() - } - setMapLayer() - osmandHelper.addMapPoint(MAP_LAYER_ID, "${chatTitle}_${message.senderUserId}", userName, userName, - chatTitle, Color.RED, ALatLon(content.location.latitude, content.location.longitude), null) - } - } - } - - fun showChatMessages(chatTitle: String) { - if (osmandHelper.isOsmandConnected()) { - val messages = telegramHelper.getChatMessages(chatTitle) - for (message in messages) { - showLocationOnMap(chatTitle, message) - } - } - } + fun showLocationOnMap(chatTitle: String, message: TdApi.Message) { + if (osmandHelper.isOsmandConnected()) { + val content = message.content + if (content is TdApi.MessageLocation) { + var userName = "" + val user = telegramHelper.getUser(message.senderUserId) + if (user != null) { + userName = "${user.firstName} ${user.lastName}".trim() + if (userName.isEmpty()) { + userName = user.username + } + if (userName.isEmpty()) { + userName = user.phoneNumber + } + } + if (userName.isEmpty()) { + userName = message.senderUserId.toString() + } + setMapLayer() + osmandHelper.addMapPoint(MAP_LAYER_ID, "${chatTitle}_${message.senderUserId}", userName, userName, + chatTitle, Color.RED, ALatLon(content.location.latitude, content.location.longitude), null) + } + } + } - fun hideChatMessages(chatTitle: String) { - if (osmandHelper.isOsmandConnected()) { - val messages = telegramHelper.getChatMessages(chatTitle) - for (message in messages) { - val user = telegramHelper.getUser(message.senderUserId) - if (user != null) { - osmandHelper.removeMapPoint(MAP_LAYER_ID, "${chatTitle}_${message.senderUserId}") - } - } - } - } + fun showChatMessages(chatTitle: String) { + if (osmandHelper.isOsmandConnected()) { + val messages = telegramHelper.getChatMessages(chatTitle) + for (message in messages) { + showLocationOnMap(chatTitle, message) + } + } + } - fun startShowingLocation() { - if (!showingLocation) { - showingLocation = true - app.startUserLocationService() - } - } + fun hideChatMessages(chatTitle: String) { + if (osmandHelper.isOsmandConnected()) { + val messages = telegramHelper.getChatMessages(chatTitle) + for (message in messages) { + val user = telegramHelper.getUser(message.senderUserId) + if (user != null) { + osmandHelper.removeMapPoint(MAP_LAYER_ID, "${chatTitle}_${message.senderUserId}") + } + } + } + } - fun stopShowingLocation() { - if (showingLocation) { - showingLocation = false - app.stopUserLocationService() - } - } + fun startShowingLocation() { + if (!showingLocation) { + showingLocation = true + app.startUserLocationService() + } + } + + fun stopShowingLocation() { + if (showingLocation) { + showingLocation = false + app.stopUserLocationService() + } + } } \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt index 3933f23dff..3c68ba23c5 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt @@ -14,716 +14,716 @@ import java.util.concurrent.ConcurrentHashMap class TelegramHelper private constructor() { - companion object { - private val log = PlatformUtil.getLog(TelegramHelper::class.java) - private const val CHATS_LIMIT = 100 - private const val CHAT_LIVE_USERS_LIMIT = 100 - private const val IGNORED_ERROR_CODE = 406 + companion object { + private val log = PlatformUtil.getLog(TelegramHelper::class.java) + private const val CHATS_LIMIT = 100 + private const val CHAT_LIVE_USERS_LIMIT = 100 + private const val IGNORED_ERROR_CODE = 406 - private var helper: TelegramHelper? = null + private var helper: TelegramHelper? = null - val instance: TelegramHelper - get() { - if (helper == null) { - helper = TelegramHelper() - } - return helper!! - } - } + val instance: TelegramHelper + get() { + if (helper == null) { + helper = TelegramHelper() + } + return helper!! + } + } - private val users = ConcurrentHashMap() - private val basicGroups = ConcurrentHashMap() - private val supergroups = ConcurrentHashMap() - private val secretChats = ConcurrentHashMap() + private val users = ConcurrentHashMap() + private val basicGroups = ConcurrentHashMap() + private val supergroups = ConcurrentHashMap() + private val secretChats = ConcurrentHashMap() - private val chats = ConcurrentHashMap() - private val chatTitles = ConcurrentHashMap() - private val chatList = TreeSet() - private val chatLiveMessages = ConcurrentHashMap() + private val chats = ConcurrentHashMap() + private val chatTitles = ConcurrentHashMap() + private val chatList = TreeSet() + private val chatLiveMessages = ConcurrentHashMap() - private val usersLiveMessages = ConcurrentHashMap() + private val usersLiveMessages = ConcurrentHashMap() - private val usersFullInfo = ConcurrentHashMap() - private val basicGroupsFullInfo = ConcurrentHashMap() - private val supergroupsFullInfo = ConcurrentHashMap() + private val usersFullInfo = ConcurrentHashMap() + private val basicGroupsFullInfo = ConcurrentHashMap() + private val supergroupsFullInfo = ConcurrentHashMap() - var appDir: String? = null - private var libraryLoaded = false - private var telegramAuthorizationRequestHandler: TelegramAuthorizationRequestHandler? = null + var appDir: String? = null + private var libraryLoaded = false + private var telegramAuthorizationRequestHandler: TelegramAuthorizationRequestHandler? = null - private var client: Client? = null + private var client: Client? = null - private var haveFullChatList: Boolean = false - private var needRefreshActiveLiveLocationMessages: Boolean = true - private var requestingActiveLiveLocationMessages: Boolean = false + private var haveFullChatList: Boolean = false + private var needRefreshActiveLiveLocationMessages: Boolean = true + private var requestingActiveLiveLocationMessages: Boolean = false - private var authorizationState: AuthorizationState? = null - private var haveAuthorization = false + private var authorizationState: AuthorizationState? = null + private var haveAuthorization = false - private val defaultHandler = DefaultHandler() - private val liveLocationMessageUpdatesHandler = LiveLocationMessageUpdatesHandler() + private val defaultHandler = DefaultHandler() + private val liveLocationMessageUpdatesHandler = LiveLocationMessageUpdatesHandler() - var listener: TelegramListener? = null - var incomingMessagesListener: TelegramIncomingMessagesListener? = null + var listener: TelegramListener? = null + var incomingMessagesListener: TelegramIncomingMessagesListener? = null - fun getChatList(): TreeSet { - synchronized(chatList) { - return TreeSet(chatList) - } - } + fun getChatList(): TreeSet { + synchronized(chatList) { + return TreeSet(chatList) + } + } - fun getChatTitles(): List { - return chatTitles.keys().toList() - } + fun getChatTitles(): List { + return chatTitles.keys().toList() + } - fun getChat(id: Long): TdApi.Chat? { - return chats[id] - } + fun getChat(id: Long): TdApi.Chat? { + return chats[id] + } - fun getUser(id: Int): TdApi.User? { - return users[id] - } + fun getUser(id: Int): TdApi.User? { + return users[id] + } - fun getChatMessages(chatTitle: String): List { - val res = mutableListOf() - for (message in usersLiveMessages.values) { - val title = chats[message.chatId]?.title - if (title == chatTitle) { - res.add(message) - } - } - return res - } + fun getChatMessages(chatTitle: String): List { + val res = mutableListOf() + for (message in usersLiveMessages.values) { + val title = chats[message.chatId]?.title + if (title == chatTitle) { + res.add(message) + } + } + return res + } - private fun updateChatTitles() { - chatTitles.clear() - for (chatEntry in chats.entries) { - chatTitles[chatEntry.value.title] = chatEntry.key - } - } + private fun updateChatTitles() { + chatTitles.clear() + for (chatEntry in chats.entries) { + chatTitles[chatEntry.value.title] = chatEntry.key + } + } - enum class TelegramAuthenticationParameterType { - PHONE_NUMBER, - CODE, - PASSWORD - } + enum class TelegramAuthenticationParameterType { + PHONE_NUMBER, + CODE, + PASSWORD + } - enum class TelegramAuthorizationState { - UNKNOWN, - WAIT_PARAMETERS, - WAIT_PHONE_NUMBER, - WAIT_CODE, - WAIT_PASSWORD, - READY, - LOGGING_OUT, - CLOSING, - CLOSED - } + enum class TelegramAuthorizationState { + UNKNOWN, + WAIT_PARAMETERS, + WAIT_PHONE_NUMBER, + WAIT_CODE, + WAIT_PASSWORD, + READY, + LOGGING_OUT, + CLOSING, + CLOSED + } - interface TelegramListener { - fun onTelegramStatusChanged(prevTelegramAuthorizationState: TelegramAuthorizationState, - newTelegramAuthorizationState: TelegramAuthorizationState) + interface TelegramListener { + fun onTelegramStatusChanged(prevTelegramAuthorizationState: TelegramAuthorizationState, + newTelegramAuthorizationState: TelegramAuthorizationState) - fun onTelegramChatsRead() - fun onTelegramChatsChanged() - fun onTelegramError(code: Int, message: String) - fun onSendLiveLicationError(code: Int, message: String) - } + fun onTelegramChatsRead() + fun onTelegramChatsChanged() + fun onTelegramError(code: Int, message: String) + fun onSendLiveLicationError(code: Int, message: String) + } - interface TelegramIncomingMessagesListener { - fun onReceiveChatLocationMessages(chatTitle: String, vararg messages: TdApi.Message) - } + interface TelegramIncomingMessagesListener { + fun onReceiveChatLocationMessages(chatTitle: String, vararg messages: TdApi.Message) + } - interface TelegramAuthorizationRequestListener { - fun onRequestTelegramAuthenticationParameter(parameterType: TelegramAuthenticationParameterType) - fun onTelegramAuthorizationRequestError(code: Int, message: String) - } + interface TelegramAuthorizationRequestListener { + fun onRequestTelegramAuthenticationParameter(parameterType: TelegramAuthenticationParameterType) + fun onTelegramAuthorizationRequestError(code: Int, message: String) + } - inner class TelegramAuthorizationRequestHandler(val telegramAuthorizationRequestListener: TelegramAuthorizationRequestListener) { + inner class TelegramAuthorizationRequestHandler(val telegramAuthorizationRequestListener: TelegramAuthorizationRequestListener) { - fun applyAuthenticationParameter(parameterType: TelegramAuthenticationParameterType, parameterValue: String) { - if (!TextUtils.isEmpty(parameterValue)) { - when (parameterType) { - PHONE_NUMBER -> client!!.send(TdApi.SetAuthenticationPhoneNumber(parameterValue, false, false), AuthorizationRequestHandler()) - CODE -> client!!.send(TdApi.CheckAuthenticationCode(parameterValue, "", ""), AuthorizationRequestHandler()) - PASSWORD -> client!!.send(TdApi.CheckAuthenticationPassword(parameterValue), AuthorizationRequestHandler()) - } - } - } - } + fun applyAuthenticationParameter(parameterType: TelegramAuthenticationParameterType, parameterValue: String) { + if (!TextUtils.isEmpty(parameterValue)) { + when (parameterType) { + PHONE_NUMBER -> client!!.send(TdApi.SetAuthenticationPhoneNumber(parameterValue, false, false), AuthorizationRequestHandler()) + CODE -> client!!.send(TdApi.CheckAuthenticationCode(parameterValue, "", ""), AuthorizationRequestHandler()) + PASSWORD -> client!!.send(TdApi.CheckAuthenticationPassword(parameterValue), AuthorizationRequestHandler()) + } + } + } + } - fun getTelegramAuthorizationState(): TelegramAuthorizationState { - val authorizationState = this.authorizationState - ?: return TelegramAuthorizationState.UNKNOWN - return when (authorizationState.constructor) { - TdApi.AuthorizationStateWaitTdlibParameters.CONSTRUCTOR -> TelegramAuthorizationState.WAIT_PARAMETERS - TdApi.AuthorizationStateWaitPhoneNumber.CONSTRUCTOR -> TelegramAuthorizationState.WAIT_PHONE_NUMBER - TdApi.AuthorizationStateWaitCode.CONSTRUCTOR -> TelegramAuthorizationState.WAIT_CODE - TdApi.AuthorizationStateWaitPassword.CONSTRUCTOR -> TelegramAuthorizationState.WAIT_PASSWORD - TdApi.AuthorizationStateReady.CONSTRUCTOR -> TelegramAuthorizationState.READY - TdApi.AuthorizationStateLoggingOut.CONSTRUCTOR -> TelegramAuthorizationState.LOGGING_OUT - TdApi.AuthorizationStateClosing.CONSTRUCTOR -> TelegramAuthorizationState.CLOSING - TdApi.AuthorizationStateClosed.CONSTRUCTOR -> TelegramAuthorizationState.CLOSED - else -> TelegramAuthorizationState.UNKNOWN - } - } + fun getTelegramAuthorizationState(): TelegramAuthorizationState { + val authorizationState = this.authorizationState + ?: return TelegramAuthorizationState.UNKNOWN + return when (authorizationState.constructor) { + TdApi.AuthorizationStateWaitTdlibParameters.CONSTRUCTOR -> TelegramAuthorizationState.WAIT_PARAMETERS + TdApi.AuthorizationStateWaitPhoneNumber.CONSTRUCTOR -> TelegramAuthorizationState.WAIT_PHONE_NUMBER + TdApi.AuthorizationStateWaitCode.CONSTRUCTOR -> TelegramAuthorizationState.WAIT_CODE + TdApi.AuthorizationStateWaitPassword.CONSTRUCTOR -> TelegramAuthorizationState.WAIT_PASSWORD + TdApi.AuthorizationStateReady.CONSTRUCTOR -> TelegramAuthorizationState.READY + TdApi.AuthorizationStateLoggingOut.CONSTRUCTOR -> TelegramAuthorizationState.LOGGING_OUT + TdApi.AuthorizationStateClosing.CONSTRUCTOR -> TelegramAuthorizationState.CLOSING + TdApi.AuthorizationStateClosed.CONSTRUCTOR -> TelegramAuthorizationState.CLOSED + else -> TelegramAuthorizationState.UNKNOWN + } + } - fun setTelegramAuthorizationRequestHandler(telegramAuthorizationRequestListener: TelegramAuthorizationRequestListener): TelegramAuthorizationRequestHandler { - val handler = TelegramAuthorizationRequestHandler(telegramAuthorizationRequestListener) - this.telegramAuthorizationRequestHandler = handler - return handler - } + fun setTelegramAuthorizationRequestHandler(telegramAuthorizationRequestListener: TelegramAuthorizationRequestListener): TelegramAuthorizationRequestHandler { + val handler = TelegramAuthorizationRequestHandler(telegramAuthorizationRequestListener) + this.telegramAuthorizationRequestHandler = handler + return handler + } - init { - try { - log.debug("Loading native tdlib...") - System.loadLibrary("tdjni") - Client.setLogVerbosityLevel(0) - libraryLoaded = true - } catch (e: Throwable) { - log.error("Failed to load tdlib", e) - } + init { + try { + log.debug("Loading native tdlib...") + System.loadLibrary("tdjni") + Client.setLogVerbosityLevel(0) + libraryLoaded = true + } catch (e: Throwable) { + log.error("Failed to load tdlib", e) + } - } + } - fun init(): Boolean { - return if (libraryLoaded) { - // create client - client = Client.create(UpdatesHandler(), null, null) - client!!.send(TdApi.GetAuthorizationState(), defaultHandler) - true - } else { - false - } - } + fun init(): Boolean { + return if (libraryLoaded) { + // create client + client = Client.create(UpdatesHandler(), null, null) + client!!.send(TdApi.GetAuthorizationState(), defaultHandler) + true + } else { + false + } + } - private fun requestChats(reload: Boolean = false) { - synchronized(chatList) { - if (reload) { - chatList.clear() - haveFullChatList = false - } - if (!haveFullChatList && CHATS_LIMIT > chatList.size) { - // have enough chats in the chat list or chat list is too small - var offsetOrder = java.lang.Long.MAX_VALUE - var offsetChatId: Long = 0 - if (!chatList.isEmpty()) { - val last = chatList.last() - offsetOrder = last.order - offsetChatId = last.chatId - } - client?.send(TdApi.GetChats(offsetOrder, offsetChatId, CHATS_LIMIT - chatList.size), { obj -> - when (obj.constructor) { - TdApi.Error.CONSTRUCTOR -> { - val error = obj as TdApi.Error - if (error.code != IGNORED_ERROR_CODE) { - listener?.onTelegramError(error.code, error.message) - } - } - TdApi.Chats.CONSTRUCTOR -> { - val chatIds = (obj as TdApi.Chats).chatIds - if (chatIds.isEmpty()) { - synchronized(chatList) { - haveFullChatList = true - } - } - // chats had already been received through updates, let's retry request - requestChats() - } - else -> listener?.onTelegramError(-1, "Receive wrong response from TDLib: $obj") - } - }) - return - } - } - updateChatTitles() - getChatRecentLocationMessages(chatTitles.keys) - listener?.onTelegramChatsRead() - } + private fun requestChats(reload: Boolean = false) { + synchronized(chatList) { + if (reload) { + chatList.clear() + haveFullChatList = false + } + if (!haveFullChatList && CHATS_LIMIT > chatList.size) { + // have enough chats in the chat list or chat list is too small + var offsetOrder = java.lang.Long.MAX_VALUE + var offsetChatId: Long = 0 + if (!chatList.isEmpty()) { + val last = chatList.last() + offsetOrder = last.order + offsetChatId = last.chatId + } + client?.send(TdApi.GetChats(offsetOrder, offsetChatId, CHATS_LIMIT - chatList.size), { obj -> + when (obj.constructor) { + TdApi.Error.CONSTRUCTOR -> { + val error = obj as TdApi.Error + if (error.code != IGNORED_ERROR_CODE) { + listener?.onTelegramError(error.code, error.message) + } + } + TdApi.Chats.CONSTRUCTOR -> { + val chatIds = (obj as TdApi.Chats).chatIds + if (chatIds.isEmpty()) { + synchronized(chatList) { + haveFullChatList = true + } + } + // chats had already been received through updates, let's retry request + requestChats() + } + else -> listener?.onTelegramError(-1, "Receive wrong response from TDLib: $obj") + } + }) + return + } + } + updateChatTitles() + getChatRecentLocationMessages(chatTitles.keys) + listener?.onTelegramChatsRead() + } - private fun getChatRecentLocationMessages(chatTitles: Set) { - if (haveAuthorization) { - for (chatTitle in chatTitles) { - val chatId = this.chatTitles[chatTitle] - if (chatId != null) { - client?.send(TdApi.SearchChatRecentLocationMessages(chatId, CHAT_LIVE_USERS_LIMIT), { obj -> - when (obj.constructor) { - TdApi.Error.CONSTRUCTOR -> { - val error = obj as TdApi.Error - val code = error.code - if (code != IGNORED_ERROR_CODE && code != 400) { - listener?.onTelegramError(code, error.message) - } - } - TdApi.Messages.CONSTRUCTOR -> { - val messages = (obj as TdApi.Messages).messages - for (message in messages) { - if (!message.isOutgoing) { - usersLiveMessages[message.id] = message - } - } - incomingMessagesListener?.onReceiveChatLocationMessages(chatTitle, *messages) - } - else -> listener?.onTelegramError(-1, "Receive wrong response from TDLib: $obj") - } - }) - } - } - } - } + private fun getChatRecentLocationMessages(chatTitles: Set) { + if (haveAuthorization) { + for (chatTitle in chatTitles) { + val chatId = this.chatTitles[chatTitle] + if (chatId != null) { + client?.send(TdApi.SearchChatRecentLocationMessages(chatId, CHAT_LIVE_USERS_LIMIT), { obj -> + when (obj.constructor) { + TdApi.Error.CONSTRUCTOR -> { + val error = obj as TdApi.Error + val code = error.code + if (code != IGNORED_ERROR_CODE && code != 400) { + listener?.onTelegramError(code, error.message) + } + } + TdApi.Messages.CONSTRUCTOR -> { + val messages = (obj as TdApi.Messages).messages + for (message in messages) { + if (!message.isOutgoing) { + usersLiveMessages[message.id] = message + } + } + incomingMessagesListener?.onReceiveChatLocationMessages(chatTitle, *messages) + } + else -> listener?.onTelegramError(-1, "Receive wrong response from TDLib: $obj") + } + }) + } + } + } + } - /** - * @chatId Id of the chat - * @livePeriod Period for which the location can be updated, in seconds; should be between 60 and 86400 for a live location and 0 otherwise. - * @latitude Latitude of the location - * @longitude Longitude of the location - */ - fun sendLiveLocationMessage(chatTitles: List, livePeriod: Int = 61, latitude: Double, longitude: Double): Boolean { - if (!requestingActiveLiveLocationMessages && haveAuthorization) { - if (needRefreshActiveLiveLocationMessages) { - getActiveLiveLocationMessages { - sendLiveLocationImpl(chatTitles, livePeriod, latitude, longitude) - } - needRefreshActiveLiveLocationMessages = false - } else { - sendLiveLocationImpl(chatTitles, livePeriod, latitude, longitude) - } - return true - } - return false - } + /** + * @chatId Id of the chat + * @livePeriod Period for which the location can be updated, in seconds; should be between 60 and 86400 for a live location and 0 otherwise. + * @latitude Latitude of the location + * @longitude Longitude of the location + */ + fun sendLiveLocationMessage(chatTitles: List, livePeriod: Int = 61, latitude: Double, longitude: Double): Boolean { + if (!requestingActiveLiveLocationMessages && haveAuthorization) { + if (needRefreshActiveLiveLocationMessages) { + getActiveLiveLocationMessages { + sendLiveLocationImpl(chatTitles, livePeriod, latitude, longitude) + } + needRefreshActiveLiveLocationMessages = false + } else { + sendLiveLocationImpl(chatTitles, livePeriod, latitude, longitude) + } + return true + } + return false + } - private fun getActiveLiveLocationMessages(onComplete: (() -> Unit)?) { - requestingActiveLiveLocationMessages = true - client?.send(TdApi.GetActiveLiveLocationMessages(), { obj -> - when (obj.constructor) { - TdApi.Error.CONSTRUCTOR -> { - val error = obj as TdApi.Error - if (error.code != IGNORED_ERROR_CODE) { - needRefreshActiveLiveLocationMessages = true - listener?.onSendLiveLicationError(error.code, error.message) - } - } - TdApi.Messages.CONSTRUCTOR -> { - val messages = (obj as TdApi.Messages).messages - chatLiveMessages.clear() - if (messages.isNotEmpty()) { - for (msg in messages) { - val chatId = msg.chatId - chatLiveMessages[chatId] = msg.id - } - } - onComplete?.invoke() - } - else -> listener?.onSendLiveLicationError(-1, "Receive wrong response from TDLib: $obj") - } - requestingActiveLiveLocationMessages = false - }) - } + private fun getActiveLiveLocationMessages(onComplete: (() -> Unit)?) { + requestingActiveLiveLocationMessages = true + client?.send(TdApi.GetActiveLiveLocationMessages(), { obj -> + when (obj.constructor) { + TdApi.Error.CONSTRUCTOR -> { + val error = obj as TdApi.Error + if (error.code != IGNORED_ERROR_CODE) { + needRefreshActiveLiveLocationMessages = true + listener?.onSendLiveLicationError(error.code, error.message) + } + } + TdApi.Messages.CONSTRUCTOR -> { + val messages = (obj as TdApi.Messages).messages + chatLiveMessages.clear() + if (messages.isNotEmpty()) { + for (msg in messages) { + val chatId = msg.chatId + chatLiveMessages[chatId] = msg.id + } + } + onComplete?.invoke() + } + else -> listener?.onSendLiveLicationError(-1, "Receive wrong response from TDLib: $obj") + } + requestingActiveLiveLocationMessages = false + }) + } - private fun sendLiveLocationImpl(chatTitles: List, livePeriod: Int = 61, latitude: Double, longitude: Double) { - val lp = livePeriod.coerceAtLeast(61) - val location = TdApi.Location(latitude, longitude) - val content = TdApi.InputMessageLocation(location, lp) + private fun sendLiveLocationImpl(chatTitles: List, livePeriod: Int = 61, latitude: Double, longitude: Double) { + val lp = livePeriod.coerceAtLeast(61) + val location = TdApi.Location(latitude, longitude) + val content = TdApi.InputMessageLocation(location, lp) - for (chatTitle in chatTitles) { - val chatId = this.chatTitles[chatTitle] - if (chatId != null) { - val msgId = chatLiveMessages[chatId] - if (msgId != null) { - if (msgId != 0L) { - client?.send(TdApi.EditMessageLiveLocation(chatId, msgId, null, location), liveLocationMessageUpdatesHandler) - } - } else { - chatLiveMessages[chatId] = 0L - client?.send(TdApi.SendMessage(chatId, 0, false, true, null, content), liveLocationMessageUpdatesHandler) - } - } - } - } + for (chatTitle in chatTitles) { + val chatId = this.chatTitles[chatTitle] + if (chatId != null) { + val msgId = chatLiveMessages[chatId] + if (msgId != null) { + if (msgId != 0L) { + client?.send(TdApi.EditMessageLiveLocation(chatId, msgId, null, location), liveLocationMessageUpdatesHandler) + } + } else { + chatLiveMessages[chatId] = 0L + client?.send(TdApi.SendMessage(chatId, 0, false, true, null, content), liveLocationMessageUpdatesHandler) + } + } + } + } - /** - * @chatId Id of the chat - * @message Text of the message - */ - fun sendTextMessage(chatId: Long, message: String): Boolean { - // initialize reply markup just for testing - //val row = arrayOf(TdApi.InlineKeyboardButton("https://telegram.org?1", TdApi.InlineKeyboardButtonTypeUrl()), TdApi.InlineKeyboardButton("https://telegram.org?2", TdApi.InlineKeyboardButtonTypeUrl()), TdApi.InlineKeyboardButton("https://telegram.org?3", TdApi.InlineKeyboardButtonTypeUrl())) - //val replyMarkup = TdApi.ReplyMarkupInlineKeyboard(arrayOf(row, row, row)) + /** + * @chatId Id of the chat + * @message Text of the message + */ + fun sendTextMessage(chatId: Long, message: String): Boolean { + // initialize reply markup just for testing + //val row = arrayOf(TdApi.InlineKeyboardButton("https://telegram.org?1", TdApi.InlineKeyboardButtonTypeUrl()), TdApi.InlineKeyboardButton("https://telegram.org?2", TdApi.InlineKeyboardButtonTypeUrl()), TdApi.InlineKeyboardButton("https://telegram.org?3", TdApi.InlineKeyboardButtonTypeUrl())) + //val replyMarkup = TdApi.ReplyMarkupInlineKeyboard(arrayOf(row, row, row)) - if (haveAuthorization) { - val content = TdApi.InputMessageText(TdApi.FormattedText(message, null), false, true) - client?.send(TdApi.SendMessage(chatId, 0, false, true, null, content), defaultHandler) - return true - } - return false - } + if (haveAuthorization) { + val content = TdApi.InputMessageText(TdApi.FormattedText(message, null), false, true) + client?.send(TdApi.SendMessage(chatId, 0, false, true, null, content), defaultHandler) + return true + } + return false + } - fun logout(): Boolean { - return if (libraryLoaded) { - haveAuthorization = false - client!!.send(TdApi.LogOut(), defaultHandler) - true - } else { - false - } - } + fun logout(): Boolean { + return if (libraryLoaded) { + haveAuthorization = false + client!!.send(TdApi.LogOut(), defaultHandler) + true + } else { + false + } + } - fun close(): Boolean { - return if (libraryLoaded) { - haveAuthorization = false - client!!.send(TdApi.Close(), defaultHandler) - true - } else { - false - } - } + fun close(): Boolean { + return if (libraryLoaded) { + haveAuthorization = false + client!!.send(TdApi.Close(), defaultHandler) + true + } else { + false + } + } - private fun setChatOrder(chat: TdApi.Chat, order: Long) { - synchronized(chatList) { - if (chat.order != 0L) { - chatList.remove(OrderedChat(chat.order, chat.id)) - } + private fun setChatOrder(chat: TdApi.Chat, order: Long) { + synchronized(chatList) { + if (chat.order != 0L) { + chatList.remove(OrderedChat(chat.order, chat.id)) + } - chat.order = order + chat.order = order - if (chat.order != 0L) { - chatList.add(OrderedChat(chat.order, chat.id)) - } - } - } + if (chat.order != 0L) { + chatList.add(OrderedChat(chat.order, chat.id)) + } + } + } - private inner class LiveLocationMessageUpdatesHandler : ResultHandler { - override fun onResult(obj: TdApi.Object) { - when (obj.constructor) { - TdApi.Error.CONSTRUCTOR -> { - val error = obj as TdApi.Error - if (error.code != IGNORED_ERROR_CODE) { - needRefreshActiveLiveLocationMessages = true - listener?.onSendLiveLicationError(error.code, error.message) - } - } - else -> { - if (obj is TdApi.Message) { - when (obj.sendingState?.constructor) { - TdApi.MessageSendingStateFailed.CONSTRUCTOR -> { - needRefreshActiveLiveLocationMessages = true - listener?.onSendLiveLicationError(-1, "Live location message ${obj.id} failed to send") - } - } - } - } - } - } - } + private inner class LiveLocationMessageUpdatesHandler : ResultHandler { + override fun onResult(obj: TdApi.Object) { + when (obj.constructor) { + TdApi.Error.CONSTRUCTOR -> { + val error = obj as TdApi.Error + if (error.code != IGNORED_ERROR_CODE) { + needRefreshActiveLiveLocationMessages = true + listener?.onSendLiveLicationError(error.code, error.message) + } + } + else -> { + if (obj is TdApi.Message) { + when (obj.sendingState?.constructor) { + TdApi.MessageSendingStateFailed.CONSTRUCTOR -> { + needRefreshActiveLiveLocationMessages = true + listener?.onSendLiveLicationError(-1, "Live location message ${obj.id} failed to send") + } + } + } + } + } + } + } - private fun onAuthorizationStateUpdated(authorizationState: AuthorizationState?) { - val prevAuthState = getTelegramAuthorizationState() - if (authorizationState != null) { - this.authorizationState = authorizationState - } - when (this.authorizationState?.constructor) { - TdApi.AuthorizationStateWaitTdlibParameters.CONSTRUCTOR -> { - log.info("Init tdlib parameters") + private fun onAuthorizationStateUpdated(authorizationState: AuthorizationState?) { + val prevAuthState = getTelegramAuthorizationState() + if (authorizationState != null) { + this.authorizationState = authorizationState + } + when (this.authorizationState?.constructor) { + TdApi.AuthorizationStateWaitTdlibParameters.CONSTRUCTOR -> { + log.info("Init tdlib parameters") - val parameters = TdApi.TdlibParameters() - parameters.databaseDirectory = File(appDir, "tdlib").absolutePath - parameters.useMessageDatabase = true - parameters.useSecretChats = true - parameters.apiId = 94575 - parameters.apiHash = "a3406de8d171bb422bb6ddf3bbd800e2" - parameters.systemLanguageCode = "en" - parameters.deviceModel = "Android" - parameters.systemVersion = "OsmAnd Telegram" - parameters.applicationVersion = "1.0" - parameters.enableStorageOptimizer = true + val parameters = TdApi.TdlibParameters() + parameters.databaseDirectory = File(appDir, "tdlib").absolutePath + parameters.useMessageDatabase = true + parameters.useSecretChats = true + parameters.apiId = 94575 + parameters.apiHash = "a3406de8d171bb422bb6ddf3bbd800e2" + parameters.systemLanguageCode = "en" + parameters.deviceModel = "Android" + parameters.systemVersion = "OsmAnd Telegram" + parameters.applicationVersion = "1.0" + parameters.enableStorageOptimizer = true - client!!.send(TdApi.SetTdlibParameters(parameters), AuthorizationRequestHandler()) - } - TdApi.AuthorizationStateWaitEncryptionKey.CONSTRUCTOR -> { - client!!.send(TdApi.CheckDatabaseEncryptionKey(), AuthorizationRequestHandler()) - } - TdApi.AuthorizationStateWaitPhoneNumber.CONSTRUCTOR -> { - log.info("Request phone number") - telegramAuthorizationRequestHandler?.telegramAuthorizationRequestListener?.onRequestTelegramAuthenticationParameter(PHONE_NUMBER) - } - TdApi.AuthorizationStateWaitCode.CONSTRUCTOR -> { - log.info("Request code") - telegramAuthorizationRequestHandler?.telegramAuthorizationRequestListener?.onRequestTelegramAuthenticationParameter(CODE) - } - TdApi.AuthorizationStateWaitPassword.CONSTRUCTOR -> { - log.info("Request password") - telegramAuthorizationRequestHandler?.telegramAuthorizationRequestListener?.onRequestTelegramAuthenticationParameter(PASSWORD) - } - TdApi.AuthorizationStateReady.CONSTRUCTOR -> { - log.info("Ready") - } - TdApi.AuthorizationStateLoggingOut.CONSTRUCTOR -> { - log.info("Logging out") - } - TdApi.AuthorizationStateClosing.CONSTRUCTOR -> { - log.info("Closing") - } - TdApi.AuthorizationStateClosed.CONSTRUCTOR -> { - log.info("Closed") - } - else -> log.error("Unsupported authorization state: " + this.authorizationState!!) - } - val wasAuthorized = haveAuthorization - haveAuthorization = this.authorizationState?.constructor == TdApi.AuthorizationStateReady.CONSTRUCTOR - if (wasAuthorized != haveAuthorization) { - needRefreshActiveLiveLocationMessages = true - if (haveAuthorization) { - requestChats(true) - } - } - val newAuthState = getTelegramAuthorizationState() - listener?.onTelegramStatusChanged(prevAuthState, newAuthState) - } + client!!.send(TdApi.SetTdlibParameters(parameters), AuthorizationRequestHandler()) + } + TdApi.AuthorizationStateWaitEncryptionKey.CONSTRUCTOR -> { + client!!.send(TdApi.CheckDatabaseEncryptionKey(), AuthorizationRequestHandler()) + } + TdApi.AuthorizationStateWaitPhoneNumber.CONSTRUCTOR -> { + log.info("Request phone number") + telegramAuthorizationRequestHandler?.telegramAuthorizationRequestListener?.onRequestTelegramAuthenticationParameter(PHONE_NUMBER) + } + TdApi.AuthorizationStateWaitCode.CONSTRUCTOR -> { + log.info("Request code") + telegramAuthorizationRequestHandler?.telegramAuthorizationRequestListener?.onRequestTelegramAuthenticationParameter(CODE) + } + TdApi.AuthorizationStateWaitPassword.CONSTRUCTOR -> { + log.info("Request password") + telegramAuthorizationRequestHandler?.telegramAuthorizationRequestListener?.onRequestTelegramAuthenticationParameter(PASSWORD) + } + TdApi.AuthorizationStateReady.CONSTRUCTOR -> { + log.info("Ready") + } + TdApi.AuthorizationStateLoggingOut.CONSTRUCTOR -> { + log.info("Logging out") + } + TdApi.AuthorizationStateClosing.CONSTRUCTOR -> { + log.info("Closing") + } + TdApi.AuthorizationStateClosed.CONSTRUCTOR -> { + log.info("Closed") + } + else -> log.error("Unsupported authorization state: " + this.authorizationState!!) + } + val wasAuthorized = haveAuthorization + haveAuthorization = this.authorizationState?.constructor == TdApi.AuthorizationStateReady.CONSTRUCTOR + if (wasAuthorized != haveAuthorization) { + needRefreshActiveLiveLocationMessages = true + if (haveAuthorization) { + requestChats(true) + } + } + val newAuthState = getTelegramAuthorizationState() + listener?.onTelegramStatusChanged(prevAuthState, newAuthState) + } - class OrderedChat internal constructor(internal val order: Long, internal val chatId: Long) : Comparable { + class OrderedChat internal constructor(internal val order: Long, internal val chatId: Long) : Comparable { - override fun compareTo(other: OrderedChat): Int { - if (this.order != other.order) { - return if (other.order < this.order) -1 else 1 - } - return if (this.chatId != other.chatId) { - if (other.chatId < this.chatId) -1 else 1 - } else 0 - } + override fun compareTo(other: OrderedChat): Int { + if (this.order != other.order) { + return if (other.order < this.order) -1 else 1 + } + return if (this.chatId != other.chatId) { + if (other.chatId < this.chatId) -1 else 1 + } else 0 + } - override fun equals(other: Any?): Boolean { - if (other == null) { - return false - } - if (other !is OrderedChat) { - return false - } - val o = other as OrderedChat? - return this.order == o!!.order && this.chatId == o.chatId - } + override fun equals(other: Any?): Boolean { + if (other == null) { + return false + } + if (other !is OrderedChat) { + return false + } + val o = other as OrderedChat? + return this.order == o!!.order && this.chatId == o.chatId + } - override fun hashCode(): Int { - return (order + chatId).hashCode() - } - } + override fun hashCode(): Int { + return (order + chatId).hashCode() + } + } - private class DefaultHandler : ResultHandler { - override fun onResult(obj: TdApi.Object) {} - } + private class DefaultHandler : ResultHandler { + override fun onResult(obj: TdApi.Object) {} + } - private inner class UpdatesHandler : ResultHandler { - override fun onResult(obj: TdApi.Object) { - when (obj.constructor) { - TdApi.UpdateAuthorizationState.CONSTRUCTOR -> onAuthorizationStateUpdated((obj as TdApi.UpdateAuthorizationState).authorizationState) + private inner class UpdatesHandler : ResultHandler { + override fun onResult(obj: TdApi.Object) { + when (obj.constructor) { + TdApi.UpdateAuthorizationState.CONSTRUCTOR -> onAuthorizationStateUpdated((obj as TdApi.UpdateAuthorizationState).authorizationState) - TdApi.UpdateUser.CONSTRUCTOR -> { - val updateUser = obj as TdApi.UpdateUser - users[updateUser.user.id] = updateUser.user - } - TdApi.UpdateUserStatus.CONSTRUCTOR -> { - val updateUserStatus = obj as TdApi.UpdateUserStatus - val user = users[updateUserStatus.userId] - synchronized(user!!) { - user.status = updateUserStatus.status - } - } - TdApi.UpdateBasicGroup.CONSTRUCTOR -> { - val updateBasicGroup = obj as TdApi.UpdateBasicGroup - basicGroups[updateBasicGroup.basicGroup.id] = updateBasicGroup.basicGroup - } - TdApi.UpdateSupergroup.CONSTRUCTOR -> { - val updateSupergroup = obj as TdApi.UpdateSupergroup - supergroups[updateSupergroup.supergroup.id] = updateSupergroup.supergroup - } - TdApi.UpdateSecretChat.CONSTRUCTOR -> { - val updateSecretChat = obj as TdApi.UpdateSecretChat - secretChats[updateSecretChat.secretChat.id] = updateSecretChat.secretChat - } + TdApi.UpdateUser.CONSTRUCTOR -> { + val updateUser = obj as TdApi.UpdateUser + users[updateUser.user.id] = updateUser.user + } + TdApi.UpdateUserStatus.CONSTRUCTOR -> { + val updateUserStatus = obj as TdApi.UpdateUserStatus + val user = users[updateUserStatus.userId] + synchronized(user!!) { + user.status = updateUserStatus.status + } + } + TdApi.UpdateBasicGroup.CONSTRUCTOR -> { + val updateBasicGroup = obj as TdApi.UpdateBasicGroup + basicGroups[updateBasicGroup.basicGroup.id] = updateBasicGroup.basicGroup + } + TdApi.UpdateSupergroup.CONSTRUCTOR -> { + val updateSupergroup = obj as TdApi.UpdateSupergroup + supergroups[updateSupergroup.supergroup.id] = updateSupergroup.supergroup + } + TdApi.UpdateSecretChat.CONSTRUCTOR -> { + val updateSecretChat = obj as TdApi.UpdateSecretChat + secretChats[updateSecretChat.secretChat.id] = updateSecretChat.secretChat + } - TdApi.UpdateNewChat.CONSTRUCTOR -> { - val updateNewChat = obj as TdApi.UpdateNewChat - val chat = updateNewChat.chat - synchronized(chat!!) { - if (chat.type !is TdApi.ChatTypeSupergroup || !(chat.type as TdApi.ChatTypeSupergroup).isChannel) { - chats[chat.id] = chat + TdApi.UpdateNewChat.CONSTRUCTOR -> { + val updateNewChat = obj as TdApi.UpdateNewChat + val chat = updateNewChat.chat + synchronized(chat!!) { + if (chat.type !is TdApi.ChatTypeSupergroup || !(chat.type as TdApi.ChatTypeSupergroup).isChannel) { + chats[chat.id] = chat - val order = chat.order - chat.order = 0 - setChatOrder(chat, order) - } - } - updateChatTitles() - listener?.onTelegramChatsChanged() - } - TdApi.UpdateChatTitle.CONSTRUCTOR -> { - val updateChat = obj as TdApi.UpdateChatTitle - val chat = chats[updateChat.chatId] - synchronized(chat!!) { - chat.title = updateChat.title - } - updateChatTitles() - listener?.onTelegramChatsChanged() - } - TdApi.UpdateChatPhoto.CONSTRUCTOR -> { - val updateChat = obj as TdApi.UpdateChatPhoto - val chat = chats[updateChat.chatId] - synchronized(chat!!) { - chat.photo = updateChat.photo - } - listener?.onTelegramChatsChanged() - } - TdApi.UpdateChatLastMessage.CONSTRUCTOR -> { - val updateChat = obj as TdApi.UpdateChatLastMessage - val chat = chats[updateChat.chatId] - synchronized(chat!!) { - chat.lastMessage = updateChat.lastMessage - setChatOrder(chat, updateChat.order) - } - } - TdApi.UpdateChatOrder.CONSTRUCTOR -> { - val updateChat = obj as TdApi.UpdateChatOrder - val chat = chats[updateChat.chatId] - synchronized(chat!!) { - setChatOrder(chat, updateChat.order) - } - listener?.onTelegramChatsChanged() - } - TdApi.UpdateChatIsPinned.CONSTRUCTOR -> { - val updateChat = obj as TdApi.UpdateChatIsPinned - val chat = chats[updateChat.chatId] - synchronized(chat!!) { - chat.isPinned = updateChat.isPinned - setChatOrder(chat, updateChat.order) - } - } - TdApi.UpdateChatReadInbox.CONSTRUCTOR -> { - val updateChat = obj as TdApi.UpdateChatReadInbox - val chat = chats[updateChat.chatId] - synchronized(chat!!) { - chat.lastReadInboxMessageId = updateChat.lastReadInboxMessageId - chat.unreadCount = updateChat.unreadCount - } - } - TdApi.UpdateChatReadOutbox.CONSTRUCTOR -> { - val updateChat = obj as TdApi.UpdateChatReadOutbox - val chat = chats[updateChat.chatId] - synchronized(chat!!) { - chat.lastReadOutboxMessageId = updateChat.lastReadOutboxMessageId - } - } - TdApi.UpdateChatUnreadMentionCount.CONSTRUCTOR -> { - val updateChat = obj as TdApi.UpdateChatUnreadMentionCount - val chat = chats[updateChat.chatId] - synchronized(chat!!) { - chat.unreadMentionCount = updateChat.unreadMentionCount - } - } - TdApi.UpdateMessageContent.CONSTRUCTOR -> { - val updateMessageContent = obj as TdApi.UpdateMessageContent - val message = usersLiveMessages[updateMessageContent.messageId] - if (message != null && !message.isOutgoing) { - message.content = updateMessageContent.newContent - val chatTitle = chats[message.chatId]?.title - if (chatTitle != null) { - incomingMessagesListener?.onReceiveChatLocationMessages(chatTitle, message) - } - } - } - TdApi.UpdateNewMessage.CONSTRUCTOR -> { - val updateNewMessage = obj as TdApi.UpdateNewMessage - val message = updateNewMessage.message - if (!message.isOutgoing && message.content is TdApi.MessageLocation) { - usersLiveMessages[message.id] = message - val chatTitle = chats[message.chatId]?.title - if (chatTitle != null) { - incomingMessagesListener?.onReceiveChatLocationMessages(chatTitle, message) - } - } - } - TdApi.UpdateMessageMentionRead.CONSTRUCTOR -> { - val updateChat = obj as TdApi.UpdateMessageMentionRead - val chat = chats[updateChat.chatId] - synchronized(chat!!) { - chat.unreadMentionCount = updateChat.unreadMentionCount - } - } - TdApi.UpdateMessageSendFailed.CONSTRUCTOR -> { - needRefreshActiveLiveLocationMessages = true - } - TdApi.UpdateMessageSendSucceeded.CONSTRUCTOR -> { - val updateMessageSendSucceeded = obj as TdApi.UpdateMessageSendSucceeded - val message = updateMessageSendSucceeded.message - chatLiveMessages[message.chatId] = message.id - } - TdApi.UpdateDeleteMessages.CONSTRUCTOR -> { - val updateDeleteMessages = obj as TdApi.UpdateDeleteMessages - if (updateDeleteMessages.isPermanent) { - val chatId = updateDeleteMessages.chatId - for (messageId in updateDeleteMessages.messageIds) { - if (chatLiveMessages[chatId] == messageId) { - chatLiveMessages.remove(chatId) - break - } - } - } - } - TdApi.UpdateChatReplyMarkup.CONSTRUCTOR -> { - val updateChat = obj as TdApi.UpdateChatReplyMarkup - val chat = chats[updateChat.chatId] - synchronized(chat!!) { - chat.replyMarkupMessageId = updateChat.replyMarkupMessageId - } - } - TdApi.UpdateChatDraftMessage.CONSTRUCTOR -> { - val updateChat = obj as TdApi.UpdateChatDraftMessage - val chat = chats[updateChat.chatId] - synchronized(chat!!) { - chat.draftMessage = updateChat.draftMessage - setChatOrder(chat, updateChat.order) - } - } - TdApi.UpdateNotificationSettings.CONSTRUCTOR -> { - val update = obj as TdApi.UpdateNotificationSettings - if (update.scope is TdApi.NotificationSettingsScopeChat) { - val chat = chats[(update.scope as TdApi.NotificationSettingsScopeChat).chatId] - synchronized(chat!!) { - chat.notificationSettings = update.notificationSettings - } - } - } + val order = chat.order + chat.order = 0 + setChatOrder(chat, order) + } + } + updateChatTitles() + listener?.onTelegramChatsChanged() + } + TdApi.UpdateChatTitle.CONSTRUCTOR -> { + val updateChat = obj as TdApi.UpdateChatTitle + val chat = chats[updateChat.chatId] + synchronized(chat!!) { + chat.title = updateChat.title + } + updateChatTitles() + listener?.onTelegramChatsChanged() + } + TdApi.UpdateChatPhoto.CONSTRUCTOR -> { + val updateChat = obj as TdApi.UpdateChatPhoto + val chat = chats[updateChat.chatId] + synchronized(chat!!) { + chat.photo = updateChat.photo + } + listener?.onTelegramChatsChanged() + } + TdApi.UpdateChatLastMessage.CONSTRUCTOR -> { + val updateChat = obj as TdApi.UpdateChatLastMessage + val chat = chats[updateChat.chatId] + synchronized(chat!!) { + chat.lastMessage = updateChat.lastMessage + setChatOrder(chat, updateChat.order) + } + } + TdApi.UpdateChatOrder.CONSTRUCTOR -> { + val updateChat = obj as TdApi.UpdateChatOrder + val chat = chats[updateChat.chatId] + synchronized(chat!!) { + setChatOrder(chat, updateChat.order) + } + listener?.onTelegramChatsChanged() + } + TdApi.UpdateChatIsPinned.CONSTRUCTOR -> { + val updateChat = obj as TdApi.UpdateChatIsPinned + val chat = chats[updateChat.chatId] + synchronized(chat!!) { + chat.isPinned = updateChat.isPinned + setChatOrder(chat, updateChat.order) + } + } + TdApi.UpdateChatReadInbox.CONSTRUCTOR -> { + val updateChat = obj as TdApi.UpdateChatReadInbox + val chat = chats[updateChat.chatId] + synchronized(chat!!) { + chat.lastReadInboxMessageId = updateChat.lastReadInboxMessageId + chat.unreadCount = updateChat.unreadCount + } + } + TdApi.UpdateChatReadOutbox.CONSTRUCTOR -> { + val updateChat = obj as TdApi.UpdateChatReadOutbox + val chat = chats[updateChat.chatId] + synchronized(chat!!) { + chat.lastReadOutboxMessageId = updateChat.lastReadOutboxMessageId + } + } + TdApi.UpdateChatUnreadMentionCount.CONSTRUCTOR -> { + val updateChat = obj as TdApi.UpdateChatUnreadMentionCount + val chat = chats[updateChat.chatId] + synchronized(chat!!) { + chat.unreadMentionCount = updateChat.unreadMentionCount + } + } + TdApi.UpdateMessageContent.CONSTRUCTOR -> { + val updateMessageContent = obj as TdApi.UpdateMessageContent + val message = usersLiveMessages[updateMessageContent.messageId] + if (message != null && !message.isOutgoing) { + message.content = updateMessageContent.newContent + val chatTitle = chats[message.chatId]?.title + if (chatTitle != null) { + incomingMessagesListener?.onReceiveChatLocationMessages(chatTitle, message) + } + } + } + TdApi.UpdateNewMessage.CONSTRUCTOR -> { + val updateNewMessage = obj as TdApi.UpdateNewMessage + val message = updateNewMessage.message + if (!message.isOutgoing && message.content is TdApi.MessageLocation) { + usersLiveMessages[message.id] = message + val chatTitle = chats[message.chatId]?.title + if (chatTitle != null) { + incomingMessagesListener?.onReceiveChatLocationMessages(chatTitle, message) + } + } + } + TdApi.UpdateMessageMentionRead.CONSTRUCTOR -> { + val updateChat = obj as TdApi.UpdateMessageMentionRead + val chat = chats[updateChat.chatId] + synchronized(chat!!) { + chat.unreadMentionCount = updateChat.unreadMentionCount + } + } + TdApi.UpdateMessageSendFailed.CONSTRUCTOR -> { + needRefreshActiveLiveLocationMessages = true + } + TdApi.UpdateMessageSendSucceeded.CONSTRUCTOR -> { + val updateMessageSendSucceeded = obj as TdApi.UpdateMessageSendSucceeded + val message = updateMessageSendSucceeded.message + chatLiveMessages[message.chatId] = message.id + } + TdApi.UpdateDeleteMessages.CONSTRUCTOR -> { + val updateDeleteMessages = obj as TdApi.UpdateDeleteMessages + if (updateDeleteMessages.isPermanent) { + val chatId = updateDeleteMessages.chatId + for (messageId in updateDeleteMessages.messageIds) { + if (chatLiveMessages[chatId] == messageId) { + chatLiveMessages.remove(chatId) + break + } + } + } + } + TdApi.UpdateChatReplyMarkup.CONSTRUCTOR -> { + val updateChat = obj as TdApi.UpdateChatReplyMarkup + val chat = chats[updateChat.chatId] + synchronized(chat!!) { + chat.replyMarkupMessageId = updateChat.replyMarkupMessageId + } + } + TdApi.UpdateChatDraftMessage.CONSTRUCTOR -> { + val updateChat = obj as TdApi.UpdateChatDraftMessage + val chat = chats[updateChat.chatId] + synchronized(chat!!) { + chat.draftMessage = updateChat.draftMessage + setChatOrder(chat, updateChat.order) + } + } + TdApi.UpdateNotificationSettings.CONSTRUCTOR -> { + val update = obj as TdApi.UpdateNotificationSettings + if (update.scope is TdApi.NotificationSettingsScopeChat) { + val chat = chats[(update.scope as TdApi.NotificationSettingsScopeChat).chatId] + synchronized(chat!!) { + chat.notificationSettings = update.notificationSettings + } + } + } - TdApi.UpdateUserFullInfo.CONSTRUCTOR -> { - val updateUserFullInfo = obj as TdApi.UpdateUserFullInfo - usersFullInfo[updateUserFullInfo.userId] = updateUserFullInfo.userFullInfo - } - TdApi.UpdateBasicGroupFullInfo.CONSTRUCTOR -> { - val updateBasicGroupFullInfo = obj as TdApi.UpdateBasicGroupFullInfo - basicGroupsFullInfo[updateBasicGroupFullInfo.basicGroupId] = updateBasicGroupFullInfo.basicGroupFullInfo - } - TdApi.UpdateSupergroupFullInfo.CONSTRUCTOR -> { - val updateSupergroupFullInfo = obj as TdApi.UpdateSupergroupFullInfo - supergroupsFullInfo[updateSupergroupFullInfo.supergroupId] = updateSupergroupFullInfo.supergroupFullInfo - } - } - } - } + TdApi.UpdateUserFullInfo.CONSTRUCTOR -> { + val updateUserFullInfo = obj as TdApi.UpdateUserFullInfo + usersFullInfo[updateUserFullInfo.userId] = updateUserFullInfo.userFullInfo + } + TdApi.UpdateBasicGroupFullInfo.CONSTRUCTOR -> { + val updateBasicGroupFullInfo = obj as TdApi.UpdateBasicGroupFullInfo + basicGroupsFullInfo[updateBasicGroupFullInfo.basicGroupId] = updateBasicGroupFullInfo.basicGroupFullInfo + } + TdApi.UpdateSupergroupFullInfo.CONSTRUCTOR -> { + val updateSupergroupFullInfo = obj as TdApi.UpdateSupergroupFullInfo + supergroupsFullInfo[updateSupergroupFullInfo.supergroupId] = updateSupergroupFullInfo.supergroupFullInfo + } + } + } + } - private inner class AuthorizationRequestHandler : ResultHandler { - override fun onResult(obj: TdApi.Object) { - when (obj.constructor) { - TdApi.Error.CONSTRUCTOR -> { - log.error("Receive an error: $obj") - val errorObj = obj as TdApi.Error - if (errorObj.code != IGNORED_ERROR_CODE) { - telegramAuthorizationRequestHandler?.telegramAuthorizationRequestListener?.onTelegramAuthorizationRequestError(errorObj.code, errorObj.message) - onAuthorizationStateUpdated(null) // repeat last action - } - } - TdApi.Ok.CONSTRUCTOR -> { - } - else -> log.error("Receive wrong response from TDLib: $obj") - }// result is already received through UpdateAuthorizationState, nothing to do - } - } + private inner class AuthorizationRequestHandler : ResultHandler { + override fun onResult(obj: TdApi.Object) { + when (obj.constructor) { + TdApi.Error.CONSTRUCTOR -> { + log.error("Receive an error: $obj") + val errorObj = obj as TdApi.Error + if (errorObj.code != IGNORED_ERROR_CODE) { + telegramAuthorizationRequestHandler?.telegramAuthorizationRequestListener?.onTelegramAuthorizationRequestError(errorObj.code, errorObj.message) + onAuthorizationStateUpdated(null) // repeat last action + } + } + TdApi.Ok.CONSTRUCTOR -> { + } + else -> log.error("Receive wrong response from TDLib: $obj") + }// result is already received through UpdateAuthorizationState, nothing to do + } + } } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/notifications/LocationNotification.kt b/OsmAnd-telegram/src/net/osmand/telegram/notifications/LocationNotification.kt new file mode 100644 index 0000000000..9d2a656b86 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/telegram/notifications/LocationNotification.kt @@ -0,0 +1,60 @@ +package net.osmand.telegram.notifications + +import android.support.v4.app.NotificationCompat +import android.support.v4.content.ContextCompat +import net.osmand.telegram.R +import net.osmand.telegram.TelegramApplication +import net.osmand.telegram.utils.OsmandFormatter +import net.osmand.util.Algorithms + +class LocationNotification(app: TelegramApplication) : TelegramNotification(app, GROUP_NAME) { + + companion object { + const val GROUP_NAME = "share_location" + } + + override val type: TelegramNotification.NotificationType + get() = TelegramNotification.NotificationType.LOCATION + + override val priority: Int + get() = NotificationCompat.PRIORITY_DEFAULT + + override val isActive: Boolean + get() { + val service = app.telegramService + return isEnabled && service != null + } + + override val isEnabled: Boolean + get() = app.settings.hasAnyChatToShareLocation() + + override val telegramNotificationId: Int + get() = TelegramNotification.LOCATION_NOTIFICATION_SERVICE_ID + + override val telegramWearableNotificationId: Int + get() = TelegramNotification.WEAR_LOCATION_NOTIFICATION_SERVICE_ID + + override fun buildNotification(wearable: Boolean): NotificationCompat.Builder { + val notificationTitle: String + val notificationText: String + val shareLocationHelper = app.shareLocationHelper + if (shareLocationHelper.sharingLocation) { + val sharedDistance = shareLocationHelper.distance.toFloat() + color = ContextCompat.getColor(app, R.color.osmand_orange) + icon = R.drawable.ic_action_polygom_dark + notificationTitle = (app.getString(R.string.sharing_location) + " • " + + Algorithms.formatDuration((shareLocationHelper.duration / 1000).toInt(), true)) + notificationText = (app.getString(R.string.shared_string_distance) + + ": " + OsmandFormatter.getFormattedDistance(sharedDistance, app)) + } else { + notificationTitle = app.getString(R.string.show_users_on_map) + notificationText = app.getString(R.string.active_chats) + ": " + app.settings.getShowOnMapChatsCount() + color = 0 + icon = R.drawable.ic_action_view + } + + return createBuilder(wearable) + .setContentTitle(notificationTitle) + .setStyle(NotificationCompat.BigTextStyle().bigText(notificationText)) + } +} diff --git a/OsmAnd-telegram/src/net/osmand/telegram/notifications/NotificationHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/notifications/NotificationHelper.kt index 5f87676f29..78c576e888 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/notifications/NotificationHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/notifications/NotificationHelper.kt @@ -5,46 +5,43 @@ import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager import android.content.Context -import android.support.v4.app.NotificationManagerCompat import net.osmand.telegram.R import net.osmand.telegram.TelegramApplication import net.osmand.telegram.notifications.TelegramNotification.NotificationType -import java.util.* class NotificationHelper(private val app: TelegramApplication) { - val shareLocationNotification = ShareLocationNotification(app) - val showLocationNotification = ShowLocationNotification(app) + val locationNotification = LocationNotification(app) - private val all = listOf(shareLocationNotification, showLocationNotification) + private val all = listOf(locationNotification) - fun buildNotification(telegramNotification: TelegramNotification): Notification { - return telegramNotification.buildNotification(false).build() - } + fun buildNotification(telegramNotification: TelegramNotification): Notification { + return telegramNotification.buildNotification(false).build() + } - fun refreshNotification(notificationType: NotificationType) { - for (notification in all) { - if (notification.type == notificationType) { - notification.refreshNotification() - break - } - } - } + fun refreshNotification(notificationType: NotificationType) { + for (notification in all) { + if (notification.type == notificationType) { + notification.refreshNotification() + break + } + } + } - @TargetApi(26) - fun createNotificationChannel() { - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { - val channel = NotificationChannel(NOTIFICATION_CHANEL_ID, - app.getString(R.string.osmand_service), NotificationManager.IMPORTANCE_LOW) - channel.enableVibration(false) - channel.description = app.getString(R.string.osmand_service_descr) - val mNotificationManager = app - .getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - mNotificationManager.createNotificationChannel(channel) - } - } + @TargetApi(26) + fun createNotificationChannel() { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + val channel = NotificationChannel(NOTIFICATION_CHANEL_ID, + app.getString(R.string.osmand_service), NotificationManager.IMPORTANCE_LOW) + channel.enableVibration(false) + channel.description = app.getString(R.string.osmand_service_descr) + val mNotificationManager = app + .getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + mNotificationManager.createNotificationChannel(channel) + } + } - companion object { - const val NOTIFICATION_CHANEL_ID = "osmand_telegram_background_service" - } + companion object { + const val NOTIFICATION_CHANEL_ID = "osmand_telegram_background_service" + } } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/notifications/ShareLocationNotification.kt b/OsmAnd-telegram/src/net/osmand/telegram/notifications/ShareLocationNotification.kt deleted file mode 100644 index e52c82386d..0000000000 --- a/OsmAnd-telegram/src/net/osmand/telegram/notifications/ShareLocationNotification.kt +++ /dev/null @@ -1,51 +0,0 @@ -package net.osmand.telegram.notifications - -import android.support.v4.app.NotificationCompat -import android.support.v4.content.ContextCompat -import net.osmand.telegram.R -import net.osmand.telegram.TelegramApplication -import net.osmand.telegram.utils.OsmandFormatter -import net.osmand.util.Algorithms - -class ShareLocationNotification(app: TelegramApplication) : TelegramNotification(app, GROUP_NAME) { - - companion object { - const val GROUP_NAME = "share_location" - } - - override val type: TelegramNotification.NotificationType - get() = TelegramNotification.NotificationType.SHARE_LOCATION - - override val priority: Int - get() = NotificationCompat.PRIORITY_DEFAULT - - override val isActive: Boolean - get() { - val service = app.myLocationService - return isEnabled && service != null - } - - override val isEnabled: Boolean - get() = app.settings.hasAnyChatToShareLocation() - - override val telegramNotificationId: Int - get() = TelegramNotification.SHARE_LOCATION_NOTIFICATION_SERVICE_ID - - override val telegramWearableNotificationId: Int - get() = TelegramNotification.WEAR_SHARE_LOCATION_NOTIFICATION_SERVICE_ID - - override fun buildNotification(wearable: Boolean): NotificationCompat.Builder { - icon = R.drawable.ic_action_polygom_dark - val shareLocationHelper = app.shareLocationHelper - val sharedDistance = shareLocationHelper.distance.toFloat() - color = ContextCompat.getColor(app, R.color.osmand_orange) - val notificationTitle = (app.getString(R.string.sharing_location) + " • " - + Algorithms.formatDuration((shareLocationHelper.duration / 1000).toInt(), true)) - val notificationText = (app.getString(R.string.shared_string_distance) - + ": " + OsmandFormatter.getFormattedDistance(sharedDistance, app)) - - return createBuilder(wearable) - .setContentTitle(notificationTitle) - .setStyle(NotificationCompat.BigTextStyle().bigText(notificationText)) - } -} diff --git a/OsmAnd-telegram/src/net/osmand/telegram/notifications/ShowLocationNotification.kt b/OsmAnd-telegram/src/net/osmand/telegram/notifications/ShowLocationNotification.kt deleted file mode 100644 index f1e38eb02d..0000000000 --- a/OsmAnd-telegram/src/net/osmand/telegram/notifications/ShowLocationNotification.kt +++ /dev/null @@ -1,45 +0,0 @@ -package net.osmand.telegram.notifications - -import android.support.v4.app.NotificationCompat -import net.osmand.telegram.R -import net.osmand.telegram.TelegramApplication - -class ShowLocationNotification(app: TelegramApplication) : TelegramNotification(app, GROUP_NAME) { - - companion object { - - const val GROUP_NAME = "show_location" - } - - override val type: TelegramNotification.NotificationType - get() = NotificationType.SHOW_LOCATION - - override val priority: Int - get() = NotificationCompat.PRIORITY_DEFAULT - - override val isActive: Boolean - get() { - val service = app.userLocationService - return isEnabled && service != null - } - - override val isEnabled: Boolean - get() = app.settings.hasAnyChatToShowOnMap() - - override val telegramNotificationId: Int - get() = TelegramNotification.SHOW_LOCATION_NOTIFICATION_SERVICE_ID - - override val telegramWearableNotificationId: Int - get() = TelegramNotification.WEAR_SHOW_LOCATION_NOTIFICATION_SERVICE_ID - - override fun buildNotification(wearable: Boolean): NotificationCompat.Builder { - val notificationTitle: String = app.getString(R.string.show_users_on_map) - val notificationText: String = app.getString(R.string.active_chats) + ": " + app.settings.getShowOnMapChatsCount() - color = 0 - icon = R.drawable.ic_action_view - - return createBuilder(wearable) - .setContentTitle(notificationTitle) - .setStyle(NotificationCompat.BigTextStyle().bigText(notificationText)) - } -} diff --git a/OsmAnd-telegram/src/net/osmand/telegram/notifications/TelegramNotification.kt b/OsmAnd-telegram/src/net/osmand/telegram/notifications/TelegramNotification.kt index 135d671c8d..521a047744 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/notifications/TelegramNotification.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/notifications/TelegramNotification.kt @@ -13,11 +13,9 @@ abstract class TelegramNotification(protected var app: TelegramApplication, val companion object { - const val SHARE_LOCATION_NOTIFICATION_SERVICE_ID = 6 - const val SHOW_LOCATION_NOTIFICATION_SERVICE_ID = 7 + const val LOCATION_NOTIFICATION_SERVICE_ID = 6 - const val WEAR_SHARE_LOCATION_NOTIFICATION_SERVICE_ID = 1006 - const val WEAR_SHOW_LOCATION_NOTIFICATION_SERVICE_ID = 1006 + const val WEAR_LOCATION_NOTIFICATION_SERVICE_ID = 1006 } protected var ongoing = true @@ -37,8 +35,7 @@ abstract class TelegramNotification(protected var app: TelegramApplication, val abstract val isEnabled: Boolean enum class NotificationType { - SHARE_LOCATION, - SHOW_LOCATION + LOCATION, } @SuppressLint("InlinedApi") diff --git a/OsmAnd-telegram/src/net/osmand/telegram/services/MyLocationService.kt b/OsmAnd-telegram/src/net/osmand/telegram/services/MyLocationService.kt deleted file mode 100644 index 5390ee6cb2..0000000000 --- a/OsmAnd-telegram/src/net/osmand/telegram/services/MyLocationService.kt +++ /dev/null @@ -1,133 +0,0 @@ -package net.osmand.telegram.services - -import android.app.Service -import android.content.Context -import android.content.Intent -import android.location.Location -import android.location.LocationListener -import android.location.LocationManager -import android.os.Binder -import android.os.Bundle -import android.os.Handler -import android.os.IBinder -import android.util.Log -import android.widget.Toast -import net.osmand.PlatformUtil -import net.osmand.telegram.R -import net.osmand.telegram.TelegramApplication - -class MyLocationService : Service(), LocationListener { - - private val binder = LocationServiceBinder() - private fun app() = application as TelegramApplication - - var handler: Handler? = null - - class LocationServiceBinder : Binder() - - override fun onBind(intent: Intent): IBinder? { - return binder - } - - fun stopIfNeeded(ctx: Context) { - val serviceIntent = Intent(ctx, MyLocationService::class.java) - ctx.stopService(serviceIntent) - } - - override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { - handler = Handler() - val app = app() - - app.myLocationService = this - - // requesting - // request location updates - val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager - try { - locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0f, this@MyLocationService) - } catch (e: SecurityException) { - Toast.makeText(this, R.string.no_location_permission, Toast.LENGTH_LONG).show() - Log.d(PlatformUtil.TAG, "Location service permission not granted") - } catch (e: IllegalArgumentException) { - Toast.makeText(this, R.string.gps_not_available, Toast.LENGTH_LONG).show() - Log.d(PlatformUtil.TAG, "GPS location provider not available") - } - - val shareLocationNotification = app.notificationHelper.shareLocationNotification - val notification = app.notificationHelper.buildNotification(shareLocationNotification) - startForeground(shareLocationNotification.telegramNotificationId, notification) - app.notificationHelper.refreshNotification(shareLocationNotification.type) - return Service.START_REDELIVER_INTENT - } - - override fun onDestroy() { - super.onDestroy() - val app = app() - app.myLocationService = null - - // remove updates - val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager - try { - locationManager.removeUpdates(this) - } catch (e: SecurityException) { - Log.d(PlatformUtil.TAG, "Location service permission not granted") - } - - // remove notification - stopForeground(java.lang.Boolean.TRUE) - } - - override fun onLocationChanged(l: Location?) { - if (l != null) { - val location = convertLocation(l) - app().shareLocationHelper.updateLocation(location) - } - } - - override fun onProviderDisabled(provider: String) { - Toast.makeText(this, getString(R.string.location_service_no_gps_available), Toast.LENGTH_LONG).show() - } - - - override fun onProviderEnabled(provider: String) {} - - - override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {} - - override fun onTaskRemoved(rootIntent: Intent) { - val app = app() - if (app.myLocationService != null) { - // Do not stop service after UI task was dismissed - //this@MyLocationService.stopSelf() - } - } - - companion object { - - fun convertLocation(l: Location?): net.osmand.Location? { - if (l == null) { - return null - } - val r = net.osmand.Location(l.provider) - r.latitude = l.latitude - r.longitude = l.longitude - r.time = l.time - if (l.hasAccuracy()) { - r.accuracy = l.accuracy - } - if (l.hasSpeed()) { - r.speed = l.speed - } - if (l.hasAltitude()) { - r.altitude = l.altitude - } - if (l.hasBearing()) { - r.bearing = l.bearing - } - if (l.hasAltitude()) { - r.altitude = l.altitude - } - return r - } - } -} diff --git a/OsmAnd-telegram/src/net/osmand/telegram/services/UserLocationService.kt b/OsmAnd-telegram/src/net/osmand/telegram/services/UserLocationService.kt deleted file mode 100644 index a7afdece16..0000000000 --- a/OsmAnd-telegram/src/net/osmand/telegram/services/UserLocationService.kt +++ /dev/null @@ -1,81 +0,0 @@ -package net.osmand.telegram.services - -import android.app.Service -import android.content.Context -import android.content.Intent -import android.os.AsyncTask -import android.os.Binder -import android.os.Handler -import android.os.IBinder -import net.osmand.telegram.TelegramApplication -import net.osmand.telegram.helpers.TelegramHelper.TelegramIncomingMessagesListener -import org.drinkless.td.libcore.telegram.TdApi -import java.util.concurrent.Executors - -class UserLocationService : Service(), TelegramIncomingMessagesListener { - - private val binder = LocationServiceBinder() - private fun app() = application as TelegramApplication - private val executor = Executors.newSingleThreadExecutor() - - var handler: Handler? = null - - class LocationServiceBinder : Binder() - - override fun onBind(intent: Intent): IBinder? { - return binder - } - - override fun onReceiveChatLocationMessages(chatTitle: String, vararg messages: TdApi.Message) { - val app = app() - if (app.settings.isShowingChatOnMap(chatTitle)) { - ShowMessagesTask(app, chatTitle).executeOnExecutor(executor, *messages) - } - } - - fun stopIfNeeded(ctx: Context) { - val serviceIntent = Intent(ctx, UserLocationService::class.java) - ctx.stopService(serviceIntent) - } - - override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { - handler = Handler() - val app = app() - - app.userLocationService = this - app.telegramHelper.incomingMessagesListener = this - - val showLocationNotification = app.notificationHelper.showLocationNotification - val notification = app.notificationHelper.buildNotification(showLocationNotification) - startForeground(showLocationNotification.telegramNotificationId, notification) - app.notificationHelper.refreshNotification(showLocationNotification.type) - return Service.START_REDELIVER_INTENT - } - - override fun onDestroy() { - super.onDestroy() - val app = app() - app.telegramHelper.incomingMessagesListener = null - app.userLocationService = null - - stopForeground(java.lang.Boolean.TRUE) - } - - override fun onTaskRemoved(rootIntent: Intent) { - val app = app() - if (app.userLocationService != null) { - // Do not stop service after UI task was dismissed - //this@MyLocationService.stopSelf() - } - } - - class ShowMessagesTask(private val app: TelegramApplication, private val chatTitle: String) : AsyncTask() { - - override fun doInBackground(vararg messages: TdApi.Message): Void? { - for (message in messages) { - app.showLocationHelper.showLocationOnMap(chatTitle, message) - } - return null - } - } -} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java b/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java index 78211827ef..9f472eaa1f 100644 --- a/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java +++ b/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java @@ -819,7 +819,10 @@ public class OsmandAidlApi { boolean updateMapLayer(AMapLayer layer) { if (layer != null && layers.containsKey(layer.getId())) { - layers.put(layer.getId(), layer); + AMapLayer existingLayer = layers.get(layer.getId()); + for (AMapPoint point : layer.getPoints()) { + existingLayer.putPoint(point); + } refreshMap(); return true; } else {