Telegram - cleanup code

This commit is contained in:
crimean 2018-06-11 19:57:33 +03:00
parent 824d608c86
commit 911126e3f9
20 changed files with 2611 additions and 2711 deletions

View file

@ -33,25 +33,14 @@
</activity> </activity>
<service <service
android:name=".services.MyLocationService" android:name=".TelegramService"
android:label="@string/process_location_service" android:label="@string/process_service"
android:stopWithTask="false"> android:stopWithTask="false">
<intent-filter> <intent-filter>
<action android:name="net.osmand.telegram.services.MyLocationService" /> <action android:name="net.osmand.telegram.TelegramService" />
</intent-filter> </intent-filter>
</service> </service>
<service
android:name=".services.UserLocationService"
android:label="@string/process_location_service"
android:stopWithTask="false">
<intent-filter>
<action android:name="net.osmand.telegram.services.UserLocationService" />
</intent-filter>
</service>
<receiver android:name=".notifications.NotificationDismissReceiver" />
</application> </application>
</manifest> </manifest>

View file

@ -27,7 +27,7 @@
<string name="shared_string_pause">Pause</string> <string name="shared_string_pause">Pause</string>
<string name="shared_string_start">Start</string> <string name="shared_string_start">Start</string>
<string name="shared_string_stop">Stop</string> <string name="shared_string_stop">Stop</string>
<string name="process_location_service">OsmAnd Telegram location service</string> <string name="process_service">OsmAnd Telegram service</string>
<string name="osmand_logo">OsmAnd logo</string> <string name="osmand_logo">OsmAnd logo</string>
<string name="install_osmand_dialog_message">You need to install free or paid version of OsmAnd first</string> <string name="install_osmand_dialog_message">You need to install free or paid version of OsmAnd first</string>
<string name="install_osmand">Install OsmAnd</string> <string name="install_osmand">Install OsmAnd</string>

View file

@ -4,143 +4,143 @@ import org.apache.commons.logging.Log;
public class PlatformUtil { public class PlatformUtil {
public static String TAG = "net.osmand"; public static String TAG = "net.osmand";
private static class OsmandLogImplementation implements Log {
private final String fullName; private static class OsmandLogImplementation implements Log {
private final String name;
public OsmandLogImplementation(String name){ private final String fullName;
this.fullName = name; private final String name;
this.name = fullName.substring(fullName.lastIndexOf('.') + 1);
}
@Override public OsmandLogImplementation(String name) {
public void trace(Object message) { this.fullName = name;
if(isTraceEnabled()){ this.name = fullName.substring(fullName.lastIndexOf('.') + 1);
android.util.Log.d(TAG, name + " " + message); }
}
}
@Override @Override
public void trace(Object message, Throwable t) { public void trace(Object message) {
if(isTraceEnabled()){ if (isTraceEnabled()) {
android.util.Log.d(TAG, name + " " + message, t); android.util.Log.d(TAG, name + " " + message);
} }
} }
@Override @Override
public void debug(Object message) { public void trace(Object message, Throwable t) {
if(isDebugEnabled()){ if (isTraceEnabled()) {
android.util.Log.d(TAG, name + " " + message); android.util.Log.d(TAG, name + " " + message, t);
} }
} }
@Override @Override
public void debug(Object message, Throwable t) { public void debug(Object message) {
if(isDebugEnabled()){ if (isDebugEnabled()) {
android.util.Log.d(TAG, name + " " + message, t); android.util.Log.d(TAG, name + " " + message);
} }
} }
@Override @Override
public void error(Object message) { public void debug(Object message, Throwable t) {
if(isErrorEnabled()){ if (isDebugEnabled()) {
android.util.Log.e(TAG, name + " " + message); android.util.Log.d(TAG, name + " " + message, t);
} }
} }
@Override @Override
public void error(Object message, Throwable t) { public void error(Object message) {
if(isErrorEnabled()){ if (isErrorEnabled()) {
android.util.Log.e(TAG, name + " " + message, t); android.util.Log.e(TAG, name + " " + message);
} }
} }
@Override @Override
public void fatal(Object message) { public void error(Object message, Throwable t) {
if(isFatalEnabled()){ if (isErrorEnabled()) {
android.util.Log.e(TAG, name + " " + message); 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 @Override
public void info(Object message) { public void fatal(Object message, Throwable t) {
if(isInfoEnabled()){ if (isFatalEnabled()) {
android.util.Log.i(TAG, name + " " + message); android.util.Log.e(TAG, name + " " + message, t);
} }
} }
@Override @Override
public void info(Object message, Throwable t) { public void info(Object message) {
if(isInfoEnabled()){ if (isInfoEnabled()) {
android.util.Log.i(TAG, name + " " + message, t); android.util.Log.i(TAG, name + " " + message);
} }
} }
@Override @Override
public boolean isTraceEnabled() { public void info(Object message, Throwable t) {
return android.util.Log.isLoggable(TAG, android.util.Log.VERBOSE); 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 @Override
public boolean isDebugEnabled() { public boolean isErrorEnabled() {
// For debug purposes always true return android.util.Log.isLoggable(TAG, android.util.Log.ERROR);
// return android.util.Log.isLoggable(TAG, android.util.Log.DEBUG); }
return true;
}
@Override @Override
public boolean isErrorEnabled() { public boolean isFatalEnabled() {
return android.util.Log.isLoggable(TAG, android.util.Log.ERROR); return android.util.Log.isLoggable(TAG, android.util.Log.ERROR);
} }
@Override @Override
public boolean isFatalEnabled() { public boolean isInfoEnabled() {
return android.util.Log.isLoggable(TAG, android.util.Log.ERROR); return android.util.Log.isLoggable(TAG, android.util.Log.INFO);
} }
@Override @Override
public boolean isInfoEnabled() { public boolean isWarnEnabled() {
return android.util.Log.isLoggable(TAG, android.util.Log.INFO); return android.util.Log.isLoggable(TAG, android.util.Log.WARN);
} }
@Override @Override
public boolean isWarnEnabled() { public void warn(Object message) {
return android.util.Log.isLoggable(TAG, android.util.Log.WARN); if (isWarnEnabled()) {
} android.util.Log.w(TAG, name + " " + message);
}
}
@Override @Override
public void warn(Object message) { public void warn(Object message, Throwable t) {
if(isWarnEnabled()){ if (isWarnEnabled()) {
android.util.Log.w(TAG, name + " " + message); android.util.Log.w(TAG, name + " " + message, t);
} }
} }
}
@Override public static Log getLog(String name) {
public void warn(Object message, Throwable t) { return new OsmandLogImplementation(name);
if(isWarnEnabled()){ }
android.util.Log.w(TAG, name + " " + message, t);
}
}
}
public static Log getLog(String name){ public static Log getLog(Class<?> cl) {
return new OsmandLogImplementation(name); return getLog(cl.getName());
} }
public static Log getLog(Class<?> cl){
return getLog(cl.getName());
}
} }

View file

@ -17,182 +17,182 @@ import net.osmand.PlatformUtil
class LoginDialogFragment : DialogFragment() { class LoginDialogFragment : DialogFragment() {
companion object { companion object {
private const val TAG = "LoginDialogFragment" private const val TAG = "LoginDialogFragment"
private val LOG = PlatformUtil.getLog(LoginDialogFragment::class.java) 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_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_CODE_PARAM_KEY = "enter_code_param_key"
private const val ENTER_PASSWORD_PARAM_KEY = "enter_password_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 SHOW_PROGRESS_PARAM_KEY = "show_progress_param_key"
fun showDialog(fragmentManager: FragmentManager, vararg loginDialogType: LoginDialogType) { fun showDialog(fragmentManager: FragmentManager, vararg loginDialogType: LoginDialogType) {
try { try {
var fragment = getFragment(fragmentManager) var fragment = getFragment(fragmentManager)
if (fragment == null) { if (fragment == null) {
fragment = LoginDialogFragment() fragment = LoginDialogFragment()
val args = Bundle() val args = Bundle()
for (t in loginDialogType) { for (t in loginDialogType) {
args.putBoolean(t.paramKey, true) args.putBoolean(t.paramKey, true)
} }
fragment.arguments = args fragment.arguments = args
fragment.show(fragmentManager, TAG) fragment.show(fragmentManager, TAG)
} else { } else {
fragment.updateDialog(*loginDialogType) fragment.updateDialog(*loginDialogType)
} }
} catch (e: RuntimeException) { } catch (e: RuntimeException) {
LOG.error(e) LOG.error(e)
} }
} }
fun dismiss(fragmentManager: FragmentManager) { fun dismiss(fragmentManager: FragmentManager) {
val loginDialogFragment = getFragment(fragmentManager) val loginDialogFragment = getFragment(fragmentManager)
loginDialogFragment?.dismissedManually = true loginDialogFragment?.dismissedManually = true
loginDialogFragment?.dismiss() loginDialogFragment?.dismiss()
} }
private fun getFragment(fragmentManager: FragmentManager): LoginDialogFragment? { private fun getFragment(fragmentManager: FragmentManager): LoginDialogFragment? {
return fragmentManager.findFragmentByTag(TAG) as LoginDialogFragment? return fragmentManager.findFragmentByTag(TAG) as LoginDialogFragment?
} }
} }
private var loginDialogActiveTypes: Set<LoginDialogType>? = null private var loginDialogActiveTypes: Set<LoginDialogType>? = null
private var dismissedManually = false private var dismissedManually = false
enum class LoginDialogType(val paramKey: String, val viewId: Int, val editorId: Int) { 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_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_CODE(ENTER_CODE_PARAM_KEY, R.id.enterCodeLayout, R.id.codeEditText),
ENTER_PASSWORD(ENTER_PASSWORD_PARAM_KEY, R.id.enterPasswordLayout, R.id.passwordEditText), ENTER_PASSWORD(ENTER_PASSWORD_PARAM_KEY, R.id.enterPasswordLayout, R.id.passwordEditText),
SHOW_PROGRESS(SHOW_PROGRESS_PARAM_KEY, R.id.progressLayout, 0); SHOW_PROGRESS(SHOW_PROGRESS_PARAM_KEY, R.id.progressLayout, 0);
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setStyle(DialogFragment.STYLE_NO_FRAME, R.style.AppTheme_NoActionbar) setStyle(DialogFragment.STYLE_NO_FRAME, R.style.AppTheme_NoActionbar)
val activity = requireActivity() val activity = requireActivity()
val window = activity.window val window = activity.window
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) 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<LoginDialogType> = HashSet() val loginDialogActiveTypes: MutableSet<LoginDialogType> = HashSet()
val args = savedInstanceState ?: arguments val args = savedInstanceState ?: arguments
if (args != null) { if (args != null) {
for (t in LoginDialogType.values()) { for (t in LoginDialogType.values()) {
if (args.getBoolean(t.paramKey, false)) { if (args.getBoolean(t.paramKey, false)) {
loginDialogActiveTypes.add(t) loginDialogActiveTypes.add(t)
} }
} }
} }
this.loginDialogActiveTypes = loginDialogActiveTypes this.loginDialogActiveTypes = loginDialogActiveTypes
val view = inflater.inflate(R.layout.login_dialog, container) val view = inflater.inflate(R.layout.login_dialog, container)
buildDialog(view) buildDialog(view)
return view return view
} }
override fun onDismiss(dialog: DialogInterface?) { override fun onDismiss(dialog: DialogInterface?) {
super.onDismiss(dialog) super.onDismiss(dialog)
if (!dismissedManually) { if (!dismissedManually) {
getMainActivity()?.closeTelegram() getMainActivity()?.closeTelegram()
} }
} }
private fun buildDialog(view: View?) { private fun buildDialog(view: View?) {
val loginDialogActiveTypes = this.loginDialogActiveTypes val loginDialogActiveTypes = this.loginDialogActiveTypes
var hasProgress = false var hasProgress = false
var focusRequested = false var focusRequested = false
for (t in LoginDialogType.values()) { for (t in LoginDialogType.values()) {
val layout: View? = view?.findViewById(t.viewId) val layout: View? = view?.findViewById(t.viewId)
val contains = loginDialogActiveTypes?.contains(t) ?: false val contains = loginDialogActiveTypes?.contains(t) ?: false
when { when {
contains -> { contains -> {
if (t == LoginDialogType.SHOW_PROGRESS) { if (t == LoginDialogType.SHOW_PROGRESS) {
hasProgress = true hasProgress = true
} }
if (layout != null) { if (layout != null) {
layout.visibility = View.VISIBLE layout.visibility = View.VISIBLE
val editText: EditText? = layout.findViewById(t.editorId) val editText: EditText? = layout.findViewById(t.editorId)
if (editText != null) { if (editText != null) {
editText.setOnEditorActionListener { _, actionId, _ -> editText.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) { if (actionId == EditorInfo.IME_ACTION_DONE) {
applyAuthParam(t, editText.text.toString()) applyAuthParam(t, editText.text.toString())
return@setOnEditorActionListener true return@setOnEditorActionListener true
} }
false false
} }
if (!focusRequested) { if (!focusRequested) {
editText.requestFocus() editText.requestFocus()
AndroidUtils.softKeyboardDelayed(editText) AndroidUtils.softKeyboardDelayed(editText)
focusRequested = true focusRequested = true
} }
} }
} }
} }
else -> layout?.visibility = View.GONE else -> layout?.visibility = View.GONE
} }
} }
val continueButton: Button? = view?.findViewById(R.id.continueButton) val continueButton: Button? = view?.findViewById(R.id.continueButton)
if (continueButton != null) { if (continueButton != null) {
continueButton.isEnabled = !hasProgress continueButton.isEnabled = !hasProgress
if (hasProgress) { if (hasProgress) {
continueButton.setOnClickListener(null) continueButton.setOnClickListener(null)
} else { } else {
continueButton.setOnClickListener { continueButton.setOnClickListener {
for (t in LoginDialogType.values()) { for (t in LoginDialogType.values()) {
val layout: View? = view.findViewById(t.viewId) val layout: View? = view.findViewById(t.viewId)
val contains = loginDialogActiveTypes?.contains(t) ?: false val contains = loginDialogActiveTypes?.contains(t) ?: false
if (contains && layout != null) { if (contains && layout != null) {
val editText: EditText? = layout.findViewById(t.editorId) val editText: EditText? = layout.findViewById(t.editorId)
if (editText != null) { if (editText != null) {
applyAuthParam(t, editText.text.toString()) applyAuthParam(t, editText.text.toString())
} }
} }
} }
} }
} }
} }
val cancelButton: Button? = view?.findViewById(R.id.calcelButton) val cancelButton: Button? = view?.findViewById(R.id.calcelButton)
cancelButton?.setOnClickListener { cancelButton?.setOnClickListener {
dismiss() dismiss()
} }
} }
private fun applyAuthParam(t: LoginDialogType, value: String) { private fun applyAuthParam(t: LoginDialogType, value: String) {
getMainActivity()?.applyAuthParam(this, t, value) getMainActivity()?.applyAuthParam(this, t, value)
} }
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
val loginDialogActiveTypes = this.loginDialogActiveTypes val loginDialogActiveTypes = this.loginDialogActiveTypes
if (loginDialogActiveTypes != null) { if (loginDialogActiveTypes != null) {
for (t in loginDialogActiveTypes) { for (t in loginDialogActiveTypes) {
outState.putBoolean(t.paramKey, true) outState.putBoolean(t.paramKey, true)
} }
} }
} }
private fun getMainActivity(): MainActivity? { private fun getMainActivity(): MainActivity? {
val activity = this.activity val activity = this.activity
return if (activity != null) { return if (activity != null) {
activity as MainActivity activity as MainActivity
} else { } else {
null null
} }
} }
fun updateDialog(vararg loginDialogType: LoginDialogType) { fun updateDialog(vararg loginDialogType: LoginDialogType) {
val loginDialogActiveTypes: MutableSet<LoginDialogType> = HashSet() val loginDialogActiveTypes: MutableSet<LoginDialogType> = HashSet()
for (t in loginDialogType) { for (t in loginDialogType) {
loginDialogActiveTypes.add(t) loginDialogActiveTypes.add(t)
} }
this.loginDialogActiveTypes = loginDialogActiveTypes this.loginDialogActiveTypes = loginDialogActiveTypes
buildDialog(view) buildDialog(view)
} }
} }

View file

@ -23,370 +23,376 @@ import org.drinkless.td.libcore.telegram.TdApi
class MainActivity : AppCompatActivity(), TelegramListener { class MainActivity : AppCompatActivity(), TelegramListener {
companion object { companion object {
private const val PERMISSION_REQUEST_LOCATION = 1 private const val PERMISSION_REQUEST_LOCATION = 1
private const val LOGIN_MENU_ID = 0 private const val LOGIN_MENU_ID = 0
private const val LOGOUT_MENU_ID = 1 private const val LOGOUT_MENU_ID = 1
private const val PROGRESS_MENU_ID = 2 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 telegramAuthorizationRequestHandler: TelegramAuthorizationRequestHandler? = null
private var paused: Boolean = false private var paused: Boolean = false
private lateinit var chatsView: RecyclerView private lateinit var chatsView: RecyclerView
private lateinit var chatViewAdapter: ChatsAdapter private lateinit var chatViewAdapter: ChatsAdapter
private lateinit var chatViewManager: RecyclerView.LayoutManager private lateinit var chatViewManager: RecyclerView.LayoutManager
private val app: TelegramApplication private val app: TelegramApplication
get() = application as TelegramApplication get() = application as TelegramApplication
private val telegramHelper get() = app.telegramHelper private val telegramHelper get() = app.telegramHelper
private val osmandHelper get() = app.osmandHelper private val osmandHelper get() = app.osmandHelper
private val settings get() = app.settings private val settings get() = app.settings
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
paused = false paused = false
chatViewManager = LinearLayoutManager(this) chatViewManager = LinearLayoutManager(this)
chatViewAdapter = ChatsAdapter() chatViewAdapter = ChatsAdapter()
chatsView = findViewById<RecyclerView>(R.id.groups_view).apply { chatsView = findViewById<RecyclerView>(R.id.groups_view).apply {
//setHasFixedSize(true) //setHasFixedSize(true)
// use a linear layout manager // use a linear layout manager
layoutManager = chatViewManager layoutManager = chatViewManager
// specify an viewAdapter (see also next example) // specify an viewAdapter (see also next example)
adapter = chatViewAdapter adapter = chatViewAdapter
} }
telegramAuthorizationRequestHandler = telegramHelper.setTelegramAuthorizationRequestHandler(object : TelegramAuthorizationRequestListener { telegramAuthorizationRequestHandler = telegramHelper.setTelegramAuthorizationRequestHandler(object : TelegramAuthorizationRequestListener {
override fun onRequestTelegramAuthenticationParameter(parameterType: TelegramAuthenticationParameterType) { override fun onRequestTelegramAuthenticationParameter(parameterType: TelegramAuthenticationParameterType) {
runOnUi { runOnUi {
showLoginDialog(parameterType) showLoginDialog(parameterType)
} }
} }
override fun onTelegramAuthorizationRequestError(code: Int, message: String) { override fun onTelegramAuthorizationRequestError(code: Int, message: String) {
runOnUi { runOnUi {
Toast.makeText(this@MainActivity, "$code - $message", Toast.LENGTH_LONG).show() Toast.makeText(this@MainActivity, "$code - $message", Toast.LENGTH_LONG).show()
} }
} }
}) })
telegramHelper.listener = this telegramHelper.listener = this
telegramHelper.init() telegramHelper.init()
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
paused = false paused = false
invalidateOptionsMenu() invalidateOptionsMenu()
updateTitle() updateTitle()
if (settings.hasAnyChatToShareLocation() && !AndroidUtils.isLocationPermissionAvailable(this)) { if (settings.hasAnyChatToShareLocation() && !AndroidUtils.isLocationPermissionAvailable(this)) {
requestLocationPermission() requestLocationPermission()
} else if (settings.hasAnyChatToShowOnMap() && osmandHelper.initialized && !osmandHelper.isOsmandBound()) { } else if (settings.hasAnyChatToShowOnMap() && osmandHelper.initialized && !osmandHelper.isOsmandBound()) {
showOsmandMissingDialog() showOsmandMissingDialog()
} }
} }
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
paused = true paused = true
} }
override fun onStop() { override fun onStop() {
super.onStop() super.onStop()
settings.save() settings.save()
} }
override fun onTelegramStatusChanged(prevTelegramAuthorizationState: TelegramAuthorizationState, override fun onDestroy() {
newTelegramAuthorizationState: TelegramAuthorizationState) { super.onDestroy()
runOnUi {
val fm = supportFragmentManager
when (newTelegramAuthorizationState) {
TelegramAuthorizationState.READY,
TelegramAuthorizationState.CLOSED,
TelegramAuthorizationState.UNKNOWN -> LoginDialogFragment.dismiss(fm)
else -> Unit
}
invalidateOptionsMenu()
updateTitle()
when (newTelegramAuthorizationState) { app.cleanupResources()
TelegramAuthorizationState.READY -> { }
updateChatsList()
}
TelegramAuthorizationState.CLOSED,
TelegramAuthorizationState.UNKNOWN -> {
chatViewAdapter.chats = emptyList()
}
else -> Unit
}
}
}
override fun onTelegramChatsRead() { override fun onTelegramStatusChanged(prevTelegramAuthorizationState: TelegramAuthorizationState,
runOnUi { newTelegramAuthorizationState: TelegramAuthorizationState) {
removeNonexistingChatsFromSettings() runOnUi {
updateChatsList() val fm = supportFragmentManager
} when (newTelegramAuthorizationState) {
} TelegramAuthorizationState.READY,
TelegramAuthorizationState.CLOSED,
TelegramAuthorizationState.UNKNOWN -> LoginDialogFragment.dismiss(fm)
else -> Unit
}
invalidateOptionsMenu()
updateTitle()
override fun onTelegramChatsChanged() { when (newTelegramAuthorizationState) {
runOnUi { TelegramAuthorizationState.READY -> {
updateChatsList() updateChatsList()
} }
} TelegramAuthorizationState.CLOSED,
TelegramAuthorizationState.UNKNOWN -> {
chatViewAdapter.chats = emptyList()
}
else -> Unit
}
}
}
override fun onTelegramError(code: Int, message: String) { override fun onTelegramChatsRead() {
runOnUi { runOnUi {
Toast.makeText(this@MainActivity, "$code - $message", Toast.LENGTH_LONG).show() removeNonexistingChatsFromSettings()
} updateChatsList()
} }
}
override fun onSendLiveLicationError(code: Int, message: String) { override fun onTelegramChatsChanged() {
log.error("Send live location error: $code - $message") runOnUi {
app.isInternetConnectionAvailable(true) updateChatsList()
} }
}
private fun removeNonexistingChatsFromSettings() { override fun onTelegramError(code: Int, message: String) {
val presentChatTitles = telegramHelper.getChatTitles() runOnUi {
settings.removeNonexistingChats(presentChatTitles) Toast.makeText(this@MainActivity, "$code - $message", Toast.LENGTH_LONG).show()
} }
}
private fun updateChatsList() { override fun onSendLiveLicationError(code: Int, message: String) {
val chatList = telegramHelper.getChatList() log.error("Send live location error: $code - $message")
val chats: MutableList<TdApi.Chat> = mutableListOf() app.isInternetConnectionAvailable(true)
for (orderedChat in chatList) { }
val chat = telegramHelper.getChat(orderedChat.chatId)
if (chat != null) {
chats.add(chat)
}
}
chatViewAdapter.chats = chats
}
fun logoutTelegram(silent: Boolean = false) { private fun removeNonexistingChatsFromSettings() {
if (telegramHelper.getTelegramAuthorizationState() == TelegramAuthorizationState.READY) { val presentChatTitles = telegramHelper.getChatTitles()
telegramHelper.logout() settings.removeNonexistingChats(presentChatTitles)
} else { }
invalidateOptionsMenu()
updateTitle()
if (!silent) {
Toast.makeText(this, R.string.not_logged_in, Toast.LENGTH_SHORT).show()
}
}
}
fun closeTelegram() { private fun updateChatsList() {
telegramHelper.close() val chatList = telegramHelper.getChatList()
} val chats: MutableList<TdApi.Chat> = 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)) { fun logoutTelegram(silent: Boolean = false) {
if (!paused) { if (telegramHelper.getTelegramAuthorizationState() == TelegramAuthorizationState.READY) {
runOnUiThread(action) 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 { fun closeTelegram() {
return when (item?.itemId) { telegramHelper.close()
LOGIN_MENU_ID -> { }
telegramHelper.init()
true
}
LOGOUT_MENU_ID -> {
logoutTelegram()
true
}
else -> super.onOptionsItemSelected(item)
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean { private fun runOnUi(action: (() -> Unit)) {
if (menu != null) { if (!paused) {
menu.clear() runOnUiThread(action)
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 createMenuItem(m: Menu, id: Int, titleRes: Int, menuItemType: Int): MenuItem { override fun onOptionsItemSelected(item: MenuItem?): Boolean {
val menuItem = m.add(0, id, 0, titleRes) return when (item?.itemId) {
menuItem.setOnMenuItemClickListener { item -> onOptionsItemSelected(item) } LOGIN_MENU_ID -> {
menuItem.setShowAsAction(menuItemType) telegramHelper.init()
return menuItem 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, "") private fun createMenuItem(m: Menu, id: Int, titleRes: Int, menuItemType: Int): MenuItem {
menuItem.actionView = layoutInflater.inflate(R.layout.action_progress_bar, null) val menuItem = m.add(0, id, 0, titleRes)
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS) menuItem.setOnMenuItemClickListener { item -> onOptionsItemSelected(item) }
return menuItem menuItem.setShowAsAction(menuItemType)
} return menuItem
}
private fun updateTitle() { private fun createProgressMenuItem(m: Menu): MenuItem {
title = when (telegramHelper.getTelegramAuthorizationState()) {
TelegramAuthorizationState.UNKNOWN, val menuItem = m.add(0, PROGRESS_MENU_ID, 0, "")
TelegramAuthorizationState.WAIT_PHONE_NUMBER, menuItem.actionView = layoutInflater.inflate(R.layout.action_progress_bar, null)
TelegramAuthorizationState.WAIT_CODE, menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
TelegramAuthorizationState.WAIT_PASSWORD, return menuItem
TelegramAuthorizationState.READY, }
TelegramAuthorizationState.CLOSED -> getString(R.string.app_name)
TelegramAuthorizationState.WAIT_PARAMETERS -> getString(R.string.initialization) + "..." private fun updateTitle() {
TelegramAuthorizationState.LOGGING_OUT -> getString(R.string.logging_out) + "..." title = when (telegramHelper.getTelegramAuthorizationState()) {
TelegramAuthorizationState.CLOSING -> getString(R.string.closing) + "..."
}
}
private fun showLoginDialog(telegramAuthenticationParameterType: TelegramAuthenticationParameterType) { TelegramAuthorizationState.UNKNOWN,
when (telegramAuthenticationParameterType) { TelegramAuthorizationState.WAIT_PHONE_NUMBER,
TelegramAuthenticationParameterType.PHONE_NUMBER -> LoginDialogFragment.showDialog(supportFragmentManager, LoginDialogType.ENTER_PHONE_NUMBER) TelegramAuthorizationState.WAIT_CODE,
TelegramAuthenticationParameterType.CODE -> LoginDialogFragment.showDialog(supportFragmentManager, LoginDialogType.ENTER_CODE) TelegramAuthorizationState.WAIT_PASSWORD,
TelegramAuthenticationParameterType.PASSWORD -> LoginDialogFragment.showDialog(supportFragmentManager, LoginDialogType.ENTER_PASSWORD) TelegramAuthorizationState.READY,
} TelegramAuthorizationState.CLOSED -> getString(R.string.app_name)
}
fun applyAuthParam(loginDialogFragment: LoginDialogFragment?, loginDialogType: LoginDialogType, text: String) { TelegramAuthorizationState.WAIT_PARAMETERS -> getString(R.string.initialization) + "..."
loginDialogFragment?.updateDialog(LoginDialogType.SHOW_PROGRESS) TelegramAuthorizationState.LOGGING_OUT -> getString(R.string.logging_out) + "..."
when (loginDialogType) { TelegramAuthorizationState.CLOSING -> getString(R.string.closing) + "..."
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
}
}
private fun requestLocationPermission() { private fun showLoginDialog(telegramAuthenticationParameterType: TelegramAuthenticationParameterType) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), PERMISSION_REQUEST_LOCATION) 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<String>, grantResults: IntArray) { fun applyAuthParam(loginDialogFragment: LoginDialogFragment?, loginDialogType: LoginDialogType, text: String) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults) loginDialogFragment?.updateDialog(LoginDialogType.SHOW_PROGRESS)
when (requestCode) { when (loginDialogType) {
PERMISSION_REQUEST_LOCATION -> { LoginDialogType.ENTER_PHONE_NUMBER -> telegramAuthorizationRequestHandler?.applyAuthenticationParameter(TelegramAuthenticationParameterType.PHONE_NUMBER, text)
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { LoginDialogType.ENTER_CODE -> telegramAuthorizationRequestHandler?.applyAuthenticationParameter(TelegramAuthenticationParameterType.CODE, text)
if (settings.hasAnyChatToShareLocation()) { LoginDialogType.ENTER_PASSWORD -> telegramAuthorizationRequestHandler?.applyAuthenticationParameter(TelegramAuthenticationParameterType.PASSWORD, text)
app.shareLocationHelper.startSharingLocation() else -> Unit
} }
} }
if (settings.hasAnyChatToShowOnMap() && osmandHelper.initialized && !osmandHelper.isOsmandBound()) {
showOsmandMissingDialog()
}
}
}
}
fun showOsmandMissingDialog() { private fun requestLocationPermission() {
OsmandMissingDialogFragment().show(supportFragmentManager, null) ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), PERMISSION_REQUEST_LOCATION)
} }
class OsmandMissingDialogFragment : DialogFragment() { override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, 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 { fun showOsmandMissingDialog() {
val builder = AlertDialog.Builder(requireContext()) OsmandMissingDialogFragment().show(supportFragmentManager, null)
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 ChatsAdapter : class OsmandMissingDialogFragment : DialogFragment() {
RecyclerView.Adapter<ChatsAdapter.ViewHolder>() {
var chats: List<TdApi.Chat> = emptyList() override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
set(value) { val builder = AlertDialog.Builder(requireContext())
field = value builder.setView(R.layout.install_osmand_dialog)
notifyDataSetChanged() .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) { inner class ChatsAdapter :
val icon: AppCompatImageView? = view.findViewById(R.id.icon) RecyclerView.Adapter<ChatsAdapter.ViewHolder>() {
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)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatsAdapter.ViewHolder { var chats: List<TdApi.Chat> = emptyList()
val view = LayoutInflater.from(parent.context).inflate(R.layout.chat_list_item, parent, false) set(value) {
return ViewHolder(view) field = value
} notifyDataSetChanged()
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) { inner class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val chatTitle = chats[position].title val icon: AppCompatImageView? = view.findViewById(R.id.icon)
holder.groupName?.text = chatTitle 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) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatsAdapter.ViewHolder {
holder.shareLocationSwitch?.isChecked = settings.isSharingLocationToChat(chatTitle) val view = LayoutInflater.from(parent.context).inflate(R.layout.chat_list_item, parent, false)
holder.shareLocationSwitch?.setOnCheckedChangeListener { view, isChecked -> return ViewHolder(view)
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) override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.showOnMapSwitch?.isChecked = settings.isShowingChatOnMap(chatTitle) val chatTitle = chats[position].title
holder.showOnMapSwitch?.setOnCheckedChangeListener { _, isChecked -> holder.groupName?.text = chatTitle
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 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
}
} }

View file

@ -12,128 +12,117 @@ import net.osmand.telegram.helpers.ShareLocationHelper
import net.osmand.telegram.helpers.ShowLocationHelper import net.osmand.telegram.helpers.ShowLocationHelper
import net.osmand.telegram.helpers.TelegramHelper import net.osmand.telegram.helpers.TelegramHelper
import net.osmand.telegram.notifications.NotificationHelper import net.osmand.telegram.notifications.NotificationHelper
import net.osmand.telegram.services.MyLocationService
import net.osmand.telegram.services.UserLocationService
import net.osmand.telegram.utils.AndroidUtils import net.osmand.telegram.utils.AndroidUtils
class TelegramApplication : Application() { class TelegramApplication : Application() {
val telegramHelper = TelegramHelper.instance val telegramHelper = TelegramHelper.instance
lateinit var settings: TelegramSettings private set lateinit var settings: TelegramSettings private set
lateinit var shareLocationHelper: ShareLocationHelper private set lateinit var shareLocationHelper: ShareLocationHelper private set
lateinit var showLocationHelper: ShowLocationHelper private set lateinit var showLocationHelper: ShowLocationHelper private set
lateinit var notificationHelper: NotificationHelper private set lateinit var notificationHelper: NotificationHelper private set
lateinit var osmandHelper: OsmandAidlHelper private set lateinit var osmandHelper: OsmandAidlHelper private set
var myLocationService: MyLocationService? = null var telegramService: TelegramService? = null
var userLocationService: UserLocationService? = null
private val uiHandler = Handler() private val uiHandler = Handler()
private val lastTimeInternetConnectionChecked: Long = 0 private val lastTimeInternetConnectionChecked: Long = 0
private var internetConnectionAvailable = true private var internetConnectionAvailable = true
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
telegramHelper.appDir = filesDir.absolutePath telegramHelper.appDir = filesDir.absolutePath
settings = TelegramSettings(this) settings = TelegramSettings(this)
osmandHelper = OsmandAidlHelper(this) osmandHelper = OsmandAidlHelper(this)
shareLocationHelper = ShareLocationHelper(this) shareLocationHelper = ShareLocationHelper(this)
showLocationHelper = ShowLocationHelper(this) showLocationHelper = ShowLocationHelper(this)
notificationHelper = NotificationHelper(this) notificationHelper = NotificationHelper(this)
if (settings.hasAnyChatToShareLocation() && AndroidUtils.isLocationPermissionAvailable(this)) { if (settings.hasAnyChatToShareLocation() && AndroidUtils.isLocationPermissionAvailable(this)) {
shareLocationHelper.startSharingLocation() shareLocationHelper.startSharingLocation()
} }
if (settings.hasAnyChatToShowOnMap()) { if (settings.hasAnyChatToShowOnMap()) {
showLocationHelper.startShowingLocation() showLocationHelper.startShowingLocation()
} }
} }
override fun onTerminate() { fun cleanupResources() {
super.onTerminate() osmandHelper.cleanupResources()
// TODO close telegram api in appropriate place telegramHelper.close()
osmandHelper.cleanupResources() }
telegramHelper.close()
}
val isWifiConnected: Boolean val isWifiConnected: Boolean
get() { get() {
val mgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val mgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val ni = mgr.activeNetworkInfo val ni = mgr.activeNetworkInfo
return ni != null && ni.type == ConnectivityManager.TYPE_WIFI return ni != null && ni.type == ConnectivityManager.TYPE_WIFI
} }
private val isInternetConnected: Boolean private val isInternetConnected: Boolean
get() { get() {
val mgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val mgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val active = mgr.activeNetworkInfo val active = mgr.activeNetworkInfo
if (active == null) { if (active == null) {
return false return false
} else { } else {
val state = active.state val state = active.state
return state != NetworkInfo.State.DISCONNECTED && state != NetworkInfo.State.DISCONNECTING return state != NetworkInfo.State.DISCONNECTED && state != NetworkInfo.State.DISCONNECTING
} }
} }
// Check internet connection available every 15 seconds // Check internet connection available every 15 seconds
val isInternetConnectionAvailable: Boolean val isInternetConnectionAvailable: Boolean
get() = isInternetConnectionAvailable(false) get() = isInternetConnectionAvailable(false)
fun isInternetConnectionAvailable(update: Boolean): Boolean { fun isInternetConnectionAvailable(update: Boolean): Boolean {
val delta = System.currentTimeMillis() - lastTimeInternetConnectionChecked val delta = System.currentTimeMillis() - lastTimeInternetConnectionChecked
if (delta < 0 || delta > 15000 || update) { if (delta < 0 || delta > 15000 || update) {
internetConnectionAvailable = isInternetConnected internetConnectionAvailable = isInternetConnected
} }
return internetConnectionAvailable return internetConnectionAvailable
} }
fun startMyLocationService(restart: Boolean = false) { private fun startTelegramService(intent: Int) {
val serviceIntent = Intent(this, MyLocationService::class.java) var i = intent
val serviceIntent = Intent(this, TelegramService::class.java)
val myLocationService = myLocationService val telegramService = telegramService
if (myLocationService != null && restart) { if (telegramService != null) {
myLocationService.stopSelf() i = intent or telegramService.usedBy
} telegramService.stopSelf()
if (myLocationService == null || restart) { }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(serviceIntent)
} else {
startService(serviceIntent)
}
}
}
fun stopMyLocationService() { serviceIntent.putExtra(TelegramService.USAGE_INTENT, i)
myLocationService?.stopIfNeeded(this) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
} startForegroundService(serviceIntent)
} else {
startService(serviceIntent)
}
}
fun startUserLocationService(restart: Boolean = false) { fun startMyLocationService() {
val serviceIntent = Intent(this, UserLocationService::class.java) startTelegramService(TelegramService.USED_BY_MY_LOCATION)
}
val userLocationService = userLocationService fun stopMyLocationService() {
if (userLocationService != null && restart) { telegramService?.stopIfNeeded(this, TelegramService.USED_BY_MY_LOCATION)
userLocationService.stopSelf() }
}
if (userLocationService == null || restart) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(serviceIntent)
} else {
startService(serviceIntent)
}
}
}
fun stopUserLocationService() { fun startUserLocationService() {
userLocationService?.stopIfNeeded(this) startTelegramService(TelegramService.USED_BY_USERS_LOCATIONS)
} }
fun runInUIThread(action: (() -> Unit)) { fun stopUserLocationService() {
uiHandler.post(action) telegramService?.stopIfNeeded(this, TelegramService.USED_BY_USERS_LOCATIONS)
} }
fun runInUIThread(action: (() -> Unit), delay: Long) { fun runInUIThread(action: (() -> Unit)) {
uiHandler.postDelayed(action, delay) uiHandler.post(action)
} }
fun runInUIThread(action: (() -> Unit), delay: Long) {
uiHandler.postDelayed(action, delay)
}
} }

View file

@ -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<TdApi.Message, Void, Void?>() {
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
}
}
}

View file

@ -6,128 +6,128 @@ import net.osmand.telegram.utils.OsmandFormatter.SpeedConstants
class TelegramSettings(private val app: TelegramApplication) { 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 SHARE_LOCATION_CHATS_KEY = "share_location_chats"
private const val SHOW_ON_MAP_CHATS_KEY = "show_on_map_chats" private const val SHOW_ON_MAP_CHATS_KEY = "show_on_map_chats"
private const val METRICS_CONSTANTS_KEY = "metrics_constants" private const val METRICS_CONSTANTS_KEY = "metrics_constants"
private const val SPEED_CONSTANTS_KEY = "speed_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<String> = emptySet() private var shareLocationChats: Set<String> = emptySet()
private var showOnMapChats: Set<String> = emptySet() private var showOnMapChats: Set<String> = emptySet()
var metricsConstants = MetricsConstants.KILOMETERS_AND_METERS var metricsConstants = MetricsConstants.KILOMETERS_AND_METERS
var speedConstants = SpeedConstants.KILOMETERS_PER_HOUR var speedConstants = SpeedConstants.KILOMETERS_PER_HOUR
var showNotificationAlways = true var showNotificationAlways = true
init { init {
read() read()
} }
fun hasAnyChatToShareLocation(): Boolean { fun hasAnyChatToShareLocation(): Boolean {
return shareLocationChats.isNotEmpty() return shareLocationChats.isNotEmpty()
} }
fun isSharingLocationToChat(chatTitle: String): Boolean { fun isSharingLocationToChat(chatTitle: String): Boolean {
return shareLocationChats.contains(chatTitle) return shareLocationChats.contains(chatTitle)
} }
fun hasAnyChatToShowOnMap(): Boolean { fun hasAnyChatToShowOnMap(): Boolean {
return showOnMapChats.isNotEmpty() return showOnMapChats.isNotEmpty()
} }
fun isShowingChatOnMap(chatTitle: String): Boolean { fun isShowingChatOnMap(chatTitle: String): Boolean {
return showOnMapChats.contains(chatTitle) return showOnMapChats.contains(chatTitle)
} }
fun removeNonexistingChats(presentChatTitles: List<String>) { fun removeNonexistingChats(presentChatTitles: List<String>) {
val shareLocationChats = shareLocationChats.toMutableList() val shareLocationChats = shareLocationChats.toMutableList()
shareLocationChats.intersect(presentChatTitles) shareLocationChats.intersect(presentChatTitles)
this.shareLocationChats = shareLocationChats.toHashSet() this.shareLocationChats = shareLocationChats.toHashSet()
val showOnMapChats = showOnMapChats.toMutableList() val showOnMapChats = showOnMapChats.toMutableList()
showOnMapChats.intersect(presentChatTitles) showOnMapChats.intersect(presentChatTitles)
this.showOnMapChats = showOnMapChats.toHashSet() this.showOnMapChats = showOnMapChats.toHashSet()
} }
fun shareLocationToChat(chatTitle: String, share: Boolean) { fun shareLocationToChat(chatTitle: String, share: Boolean) {
val shareLocationChats = shareLocationChats.toMutableList() val shareLocationChats = shareLocationChats.toMutableList()
if (share) { if (share) {
shareLocationChats.add(chatTitle) shareLocationChats.add(chatTitle)
} else { } else {
shareLocationChats.remove(chatTitle) shareLocationChats.remove(chatTitle)
} }
this.shareLocationChats = shareLocationChats.toHashSet() this.shareLocationChats = shareLocationChats.toHashSet()
} }
fun showChatOnMap(chatTitle: String, show: Boolean) { fun showChatOnMap(chatTitle: String, show: Boolean) {
val showOnMapChats = showOnMapChats.toMutableList() val showOnMapChats = showOnMapChats.toMutableList()
if (show) { if (show) {
showOnMapChats.add(chatTitle) showOnMapChats.add(chatTitle)
} else { } else {
showOnMapChats.remove(chatTitle) showOnMapChats.remove(chatTitle)
} }
this.showOnMapChats = showOnMapChats.toHashSet() this.showOnMapChats = showOnMapChats.toHashSet()
} }
fun getShareLocationChats() = ArrayList(shareLocationChats) fun getShareLocationChats() = ArrayList(shareLocationChats)
fun getShowOnMapChats() = ArrayList(showOnMapChats) fun getShowOnMapChats() = ArrayList(showOnMapChats)
fun getShowOnMapChatsCount() = showOnMapChats.size fun getShowOnMapChatsCount() = showOnMapChats.size
fun save() { fun save() {
val prefs = app.getSharedPreferences(SETTINGS_NAME, Context.MODE_PRIVATE) val prefs = app.getSharedPreferences(SETTINGS_NAME, Context.MODE_PRIVATE)
val edit = prefs.edit() val edit = prefs.edit()
val shareLocationChatsSet = mutableSetOf<String>() val shareLocationChatsSet = mutableSetOf<String>()
val shareLocationChats = ArrayList(shareLocationChats) val shareLocationChats = ArrayList(shareLocationChats)
for (chatTitle in shareLocationChats) { for (chatTitle in shareLocationChats) {
shareLocationChatsSet.add(chatTitle) shareLocationChatsSet.add(chatTitle)
} }
edit.putStringSet(SHARE_LOCATION_CHATS_KEY, shareLocationChatsSet) edit.putStringSet(SHARE_LOCATION_CHATS_KEY, shareLocationChatsSet)
val showOnMapChatsSet = mutableSetOf<String>() val showOnMapChatsSet = mutableSetOf<String>()
val showOnMapChats = ArrayList(showOnMapChats) val showOnMapChats = ArrayList(showOnMapChats)
for (chatTitle in showOnMapChats) { for (chatTitle in showOnMapChats) {
showOnMapChatsSet.add(chatTitle) showOnMapChatsSet.add(chatTitle)
} }
edit.putStringSet(SHOW_ON_MAP_CHATS_KEY, showOnMapChatsSet) edit.putStringSet(SHOW_ON_MAP_CHATS_KEY, showOnMapChatsSet)
edit.putString(METRICS_CONSTANTS_KEY, metricsConstants.name) edit.putString(METRICS_CONSTANTS_KEY, metricsConstants.name)
edit.putString(SPEED_CONSTANTS_KEY, speedConstants.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() { fun read() {
val prefs = app.getSharedPreferences(SETTINGS_NAME, Context.MODE_PRIVATE) val prefs = app.getSharedPreferences(SETTINGS_NAME, Context.MODE_PRIVATE)
val shareLocationChats = mutableSetOf<String>() val shareLocationChats = mutableSetOf<String>()
val shareLocationChatsSet = prefs.getStringSet(SHARE_LOCATION_CHATS_KEY, mutableSetOf()) val shareLocationChatsSet = prefs.getStringSet(SHARE_LOCATION_CHATS_KEY, mutableSetOf())
for (chatTitle in shareLocationChatsSet) { for (chatTitle in shareLocationChatsSet) {
shareLocationChats.add(chatTitle) shareLocationChats.add(chatTitle)
} }
this.shareLocationChats = shareLocationChats this.shareLocationChats = shareLocationChats
val showOnMapChats = mutableSetOf<String>() val showOnMapChats = mutableSetOf<String>()
val showOnMapChatsSet = prefs.getStringSet(SHOW_ON_MAP_CHATS_KEY, mutableSetOf()) val showOnMapChatsSet = prefs.getStringSet(SHOW_ON_MAP_CHATS_KEY, mutableSetOf())
for (chatTitle in showOnMapChatsSet) { for (chatTitle in showOnMapChatsSet) {
showOnMapChats.add(chatTitle) showOnMapChats.add(chatTitle)
} }
this.showOnMapChats = showOnMapChats this.showOnMapChats = showOnMapChats
metricsConstants = MetricsConstants.valueOf(prefs.getString(METRICS_CONSTANTS_KEY, MetricsConstants.KILOMETERS_AND_METERS.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)) 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)
} }
} }

View file

@ -6,88 +6,88 @@ import net.osmand.telegram.notifications.TelegramNotification.NotificationType
class ShareLocationHelper(private val app: TelegramApplication) { class ShareLocationHelper(private val app: TelegramApplication) {
companion object { companion object {
const val MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC = 60 * 60 * 24 - 1 // day const val MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC = 60 * 60 * 24 - 1 // day
} }
var sharingLocation: Boolean = false var sharingLocation: Boolean = false
private set private set
var duration: Long = 0 var duration: Long = 0
private set private set
var distance: Int = 0 var distance: Int = 0
private set 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 private var lastLocation: Location? = null
set(value) { set(value) {
if (lastTimeInMillis == 0L) { if (lastTimeInMillis == 0L) {
lastTimeInMillis = System.currentTimeMillis() lastTimeInMillis = System.currentTimeMillis()
} else { } else {
val currentTimeInMillis = System.currentTimeMillis() val currentTimeInMillis = System.currentTimeMillis()
duration += currentTimeInMillis - lastTimeInMillis duration += currentTimeInMillis - lastTimeInMillis
lastTimeInMillis = currentTimeInMillis lastTimeInMillis = currentTimeInMillis
} }
if (lastLocation != null && value != null) { if (lastLocation != null && value != null) {
distance += value.distanceTo(lastLocation).toInt() distance += value.distanceTo(lastLocation).toInt()
} }
field = value field = value
} }
fun updateLocation(location: Location?) { fun updateLocation(location: Location?) {
lastLocation = location lastLocation = location
if (location != null && app.isInternetConnectionAvailable) { if (location != null && app.isInternetConnectionAvailable) {
val shareLocationChats = app.settings.getShareLocationChats() val shareLocationChats = app.settings.getShareLocationChats()
if (shareLocationChats.isNotEmpty()) { if (shareLocationChats.isNotEmpty()) {
app.telegramHelper.sendLiveLocationMessage(shareLocationChats, MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC, location.latitude, location.longitude) app.telegramHelper.sendLiveLocationMessage(shareLocationChats, MAX_LOCATION_MESSAGE_LIVE_PERIOD_SEC, location.latitude, location.longitude)
} }
lastLocationMessageSentTime = System.currentTimeMillis() lastLocationMessageSentTime = System.currentTimeMillis()
} }
refreshNotification() refreshNotification()
} }
fun startSharingLocation() { fun startSharingLocation() {
if (!sharingLocation) { if (!sharingLocation) {
sharingLocation = true sharingLocation = true
app.startMyLocationService() app.startMyLocationService()
refreshNotification() refreshNotification()
} }
} }
fun stopSharingLocation() { fun stopSharingLocation() {
if (sharingLocation) { if (sharingLocation) {
sharingLocation = false sharingLocation = false
app.stopMyLocationService() app.stopMyLocationService()
lastLocation = null lastLocation = null
lastTimeInMillis = 0L lastTimeInMillis = 0L
distance = 0 distance = 0
duration = 0 duration = 0
refreshNotification() refreshNotification()
} }
} }
fun pauseSharingLocation() { fun pauseSharingLocation() {
sharingLocation = false sharingLocation = false
app.stopMyLocationService() app.stopMyLocationService()
lastLocation = null lastLocation = null
lastTimeInMillis = 0L lastTimeInMillis = 0L
refreshNotification() refreshNotification()
} }
private fun refreshNotification() { private fun refreshNotification() {
app.runInUIThread { app.runInUIThread {
app.notificationHelper.refreshNotification(NotificationType.SHARE_LOCATION) app.notificationHelper.refreshNotification(NotificationType.LOCATION)
} }
} }
} }

View file

@ -7,77 +7,77 @@ import org.drinkless.td.libcore.telegram.TdApi
class ShowLocationHelper(private val app: TelegramApplication) { class ShowLocationHelper(private val app: TelegramApplication) {
companion object { companion object {
private const val MAP_LAYER_ID = "telegram_layer" private const val MAP_LAYER_ID = "telegram_layer"
} }
private val telegramHelper = app.telegramHelper private val telegramHelper = app.telegramHelper
private val osmandHelper = app.osmandHelper private val osmandHelper = app.osmandHelper
var showingLocation: Boolean = false var showingLocation: Boolean = false
private set private set
private fun setMapLayer() { private fun setMapLayer() {
osmandHelper.addMapLayer(MAP_LAYER_ID, "Telegram", 5.5f, null) osmandHelper.addMapLayer(MAP_LAYER_ID, "Telegram", 5.5f, null)
} }
fun showLocationOnMap(chatTitle: String, message: TdApi.Message) { fun showLocationOnMap(chatTitle: String, message: TdApi.Message) {
if (osmandHelper.isOsmandConnected()) { if (osmandHelper.isOsmandConnected()) {
val content = message.content val content = message.content
if (content is TdApi.MessageLocation) { if (content is TdApi.MessageLocation) {
var userName = "" var userName = ""
val user = telegramHelper.getUser(message.senderUserId) val user = telegramHelper.getUser(message.senderUserId)
if (user != null) { if (user != null) {
userName = "${user.firstName} ${user.lastName}".trim() userName = "${user.firstName} ${user.lastName}".trim()
if (userName.isEmpty()) { if (userName.isEmpty()) {
userName = user.username userName = user.username
} }
if (userName.isEmpty()) { if (userName.isEmpty()) {
userName = user.phoneNumber userName = user.phoneNumber
} }
} }
if (userName.isEmpty()) { if (userName.isEmpty()) {
userName = message.senderUserId.toString() userName = message.senderUserId.toString()
} }
setMapLayer() setMapLayer()
osmandHelper.addMapPoint(MAP_LAYER_ID, "${chatTitle}_${message.senderUserId}", userName, userName, osmandHelper.addMapPoint(MAP_LAYER_ID, "${chatTitle}_${message.senderUserId}", userName, userName,
chatTitle, Color.RED, ALatLon(content.location.latitude, content.location.longitude), null) chatTitle, Color.RED, ALatLon(content.location.latitude, content.location.longitude), null)
} }
} }
} }
fun showChatMessages(chatTitle: String) { fun showChatMessages(chatTitle: String) {
if (osmandHelper.isOsmandConnected()) { if (osmandHelper.isOsmandConnected()) {
val messages = telegramHelper.getChatMessages(chatTitle) val messages = telegramHelper.getChatMessages(chatTitle)
for (message in messages) { for (message in messages) {
showLocationOnMap(chatTitle, message) showLocationOnMap(chatTitle, message)
} }
} }
} }
fun hideChatMessages(chatTitle: String) { fun hideChatMessages(chatTitle: String) {
if (osmandHelper.isOsmandConnected()) { if (osmandHelper.isOsmandConnected()) {
val messages = telegramHelper.getChatMessages(chatTitle) val messages = telegramHelper.getChatMessages(chatTitle)
for (message in messages) { for (message in messages) {
val user = telegramHelper.getUser(message.senderUserId) val user = telegramHelper.getUser(message.senderUserId)
if (user != null) { if (user != null) {
osmandHelper.removeMapPoint(MAP_LAYER_ID, "${chatTitle}_${message.senderUserId}") osmandHelper.removeMapPoint(MAP_LAYER_ID, "${chatTitle}_${message.senderUserId}")
} }
} }
} }
} }
fun startShowingLocation() { fun startShowingLocation() {
if (!showingLocation) { if (!showingLocation) {
showingLocation = true showingLocation = true
app.startUserLocationService() app.startUserLocationService()
} }
} }
fun stopShowingLocation() { fun stopShowingLocation() {
if (showingLocation) { if (showingLocation) {
showingLocation = false showingLocation = false
app.stopUserLocationService() app.stopUserLocationService()
} }
} }
} }

View file

@ -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))
}
}

View file

@ -5,46 +5,43 @@ import android.app.Notification
import android.app.NotificationChannel import android.app.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
import android.content.Context import android.content.Context
import android.support.v4.app.NotificationManagerCompat
import net.osmand.telegram.R import net.osmand.telegram.R
import net.osmand.telegram.TelegramApplication import net.osmand.telegram.TelegramApplication
import net.osmand.telegram.notifications.TelegramNotification.NotificationType import net.osmand.telegram.notifications.TelegramNotification.NotificationType
import java.util.*
class NotificationHelper(private val app: TelegramApplication) { class NotificationHelper(private val app: TelegramApplication) {
val shareLocationNotification = ShareLocationNotification(app) val locationNotification = LocationNotification(app)
val showLocationNotification = ShowLocationNotification(app)
private val all = listOf(shareLocationNotification, showLocationNotification) private val all = listOf(locationNotification)
fun buildNotification(telegramNotification: TelegramNotification): Notification { fun buildNotification(telegramNotification: TelegramNotification): Notification {
return telegramNotification.buildNotification(false).build() return telegramNotification.buildNotification(false).build()
} }
fun refreshNotification(notificationType: NotificationType) { fun refreshNotification(notificationType: NotificationType) {
for (notification in all) { for (notification in all) {
if (notification.type == notificationType) { if (notification.type == notificationType) {
notification.refreshNotification() notification.refreshNotification()
break break
} }
} }
} }
@TargetApi(26) @TargetApi(26)
fun createNotificationChannel() { fun createNotificationChannel() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val channel = NotificationChannel(NOTIFICATION_CHANEL_ID, val channel = NotificationChannel(NOTIFICATION_CHANEL_ID,
app.getString(R.string.osmand_service), NotificationManager.IMPORTANCE_LOW) app.getString(R.string.osmand_service), NotificationManager.IMPORTANCE_LOW)
channel.enableVibration(false) channel.enableVibration(false)
channel.description = app.getString(R.string.osmand_service_descr) channel.description = app.getString(R.string.osmand_service_descr)
val mNotificationManager = app val mNotificationManager = app
.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager .getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
mNotificationManager.createNotificationChannel(channel) mNotificationManager.createNotificationChannel(channel)
} }
} }
companion object { companion object {
const val NOTIFICATION_CHANEL_ID = "osmand_telegram_background_service" const val NOTIFICATION_CHANEL_ID = "osmand_telegram_background_service"
} }
} }

View file

@ -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))
}
}

View file

@ -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))
}
}

View file

@ -13,11 +13,9 @@ abstract class TelegramNotification(protected var app: TelegramApplication, val
companion object { companion object {
const val SHARE_LOCATION_NOTIFICATION_SERVICE_ID = 6 const val LOCATION_NOTIFICATION_SERVICE_ID = 6
const val SHOW_LOCATION_NOTIFICATION_SERVICE_ID = 7
const val WEAR_SHARE_LOCATION_NOTIFICATION_SERVICE_ID = 1006 const val WEAR_LOCATION_NOTIFICATION_SERVICE_ID = 1006
const val WEAR_SHOW_LOCATION_NOTIFICATION_SERVICE_ID = 1006
} }
protected var ongoing = true protected var ongoing = true
@ -37,8 +35,7 @@ abstract class TelegramNotification(protected var app: TelegramApplication, val
abstract val isEnabled: Boolean abstract val isEnabled: Boolean
enum class NotificationType { enum class NotificationType {
SHARE_LOCATION, LOCATION,
SHOW_LOCATION
} }
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")

View file

@ -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
}
}
}

View file

@ -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<TdApi.Message, Void, Void?>() {
override fun doInBackground(vararg messages: TdApi.Message): Void? {
for (message in messages) {
app.showLocationHelper.showLocationOnMap(chatTitle, message)
}
return null
}
}
}

View file

@ -819,7 +819,10 @@ public class OsmandAidlApi {
boolean updateMapLayer(AMapLayer layer) { boolean updateMapLayer(AMapLayer layer) {
if (layer != null && layers.containsKey(layer.getId())) { 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(); refreshMap();
return true; return true;
} else { } else {