Telegram - cleanup code
This commit is contained in:
parent
824d608c86
commit
911126e3f9
20 changed files with 2611 additions and 2711 deletions
|
@ -33,25 +33,14 @@
|
|||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".services.MyLocationService"
|
||||
android:label="@string/process_location_service"
|
||||
android:name=".TelegramService"
|
||||
android:label="@string/process_service"
|
||||
android:stopWithTask="false">
|
||||
<intent-filter>
|
||||
<action android:name="net.osmand.telegram.services.MyLocationService" />
|
||||
<action android:name="net.osmand.telegram.TelegramService" />
|
||||
</intent-filter>
|
||||
</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>
|
||||
|
||||
</manifest>
|
|
@ -27,7 +27,7 @@
|
|||
<string name="shared_string_pause">Pause</string>
|
||||
<string name="shared_string_start">Start</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="install_osmand_dialog_message">You need to install free or paid version of OsmAnd first</string>
|
||||
<string name="install_osmand">Install OsmAnd</string>
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<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) {
|
||||
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<LoginDialogType> = HashSet()
|
||||
val loginDialogActiveTypes: MutableSet<LoginDialogType> = 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<LoginDialogType> = HashSet()
|
||||
for (t in loginDialogType) {
|
||||
loginDialogActiveTypes.add(t)
|
||||
}
|
||||
this.loginDialogActiveTypes = loginDialogActiveTypes
|
||||
fun updateDialog(vararg loginDialogType: LoginDialogType) {
|
||||
val loginDialogActiveTypes: MutableSet<LoginDialogType> = HashSet()
|
||||
for (t in loginDialogType) {
|
||||
loginDialogActiveTypes.add(t)
|
||||
}
|
||||
this.loginDialogActiveTypes = loginDialogActiveTypes
|
||||
|
||||
buildDialog(view)
|
||||
}
|
||||
buildDialog(view)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<RecyclerView>(R.id.groups_view).apply {
|
||||
//setHasFixedSize(true)
|
||||
chatsView = findViewById<RecyclerView>(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<TdApi.Chat> = 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<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)) {
|
||||
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<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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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<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 {
|
||||
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<ChatsAdapter.ViewHolder>() {
|
||||
class OsmandMissingDialogFragment : DialogFragment() {
|
||||
|
||||
var chats: List<TdApi.Chat> = 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<ChatsAdapter.ViewHolder>() {
|
||||
|
||||
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<TdApi.Chat> = 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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
169
OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt
Normal file
169
OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<String> = emptySet()
|
||||
private var showOnMapChats: Set<String> = emptySet()
|
||||
private var shareLocationChats: Set<String> = emptySet()
|
||||
private var showOnMapChats: Set<String> = 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<String>) {
|
||||
val shareLocationChats = shareLocationChats.toMutableList()
|
||||
shareLocationChats.intersect(presentChatTitles)
|
||||
this.shareLocationChats = shareLocationChats.toHashSet()
|
||||
fun removeNonexistingChats(presentChatTitles: List<String>) {
|
||||
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<String>()
|
||||
val shareLocationChats = ArrayList(shareLocationChats)
|
||||
for (chatTitle in shareLocationChats) {
|
||||
shareLocationChatsSet.add(chatTitle)
|
||||
}
|
||||
edit.putStringSet(SHARE_LOCATION_CHATS_KEY, shareLocationChatsSet)
|
||||
val shareLocationChatsSet = mutableSetOf<String>()
|
||||
val shareLocationChats = ArrayList(shareLocationChats)
|
||||
for (chatTitle in shareLocationChats) {
|
||||
shareLocationChatsSet.add(chatTitle)
|
||||
}
|
||||
edit.putStringSet(SHARE_LOCATION_CHATS_KEY, shareLocationChatsSet)
|
||||
|
||||
val showOnMapChatsSet = mutableSetOf<String>()
|
||||
val showOnMapChats = ArrayList(showOnMapChats)
|
||||
for (chatTitle in showOnMapChats) {
|
||||
showOnMapChatsSet.add(chatTitle)
|
||||
}
|
||||
edit.putStringSet(SHOW_ON_MAP_CHATS_KEY, showOnMapChatsSet)
|
||||
val showOnMapChatsSet = mutableSetOf<String>()
|
||||
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<String>()
|
||||
val shareLocationChatsSet = prefs.getStringSet(SHARE_LOCATION_CHATS_KEY, mutableSetOf())
|
||||
for (chatTitle in shareLocationChatsSet) {
|
||||
shareLocationChats.add(chatTitle)
|
||||
}
|
||||
this.shareLocationChats = shareLocationChats
|
||||
val shareLocationChats = mutableSetOf<String>()
|
||||
val shareLocationChatsSet = prefs.getStringSet(SHARE_LOCATION_CHATS_KEY, mutableSetOf())
|
||||
for (chatTitle in shareLocationChatsSet) {
|
||||
shareLocationChats.add(chatTitle)
|
||||
}
|
||||
this.shareLocationChats = shareLocationChats
|
||||
|
||||
val showOnMapChats = mutableSetOf<String>()
|
||||
val showOnMapChatsSet = prefs.getStringSet(SHOW_ON_MAP_CHATS_KEY, mutableSetOf())
|
||||
for (chatTitle in showOnMapChatsSet) {
|
||||
showOnMapChats.add(chatTitle)
|
||||
}
|
||||
this.showOnMapChats = showOnMapChats
|
||||
val showOnMapChats = mutableSetOf<String>()
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 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 showChatMessages(chatTitle: String) {
|
||||
if (osmandHelper.isOsmandConnected()) {
|
||||
val messages = telegramHelper.getChatMessages(chatTitle)
|
||||
for (message in messages) {
|
||||
showLocationOnMap(chatTitle, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 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 startShowingLocation() {
|
||||
if (!showingLocation) {
|
||||
showingLocation = true
|
||||
app.startUserLocationService()
|
||||
}
|
||||
}
|
||||
fun startShowingLocation() {
|
||||
if (!showingLocation) {
|
||||
showingLocation = true
|
||||
app.startUserLocationService()
|
||||
}
|
||||
}
|
||||
|
||||
fun stopShowingLocation() {
|
||||
if (showingLocation) {
|
||||
showingLocation = false
|
||||
app.stopUserLocationService()
|
||||
}
|
||||
}
|
||||
fun stopShowingLocation() {
|
||||
if (showingLocation) {
|
||||
showingLocation = false
|
||||
app.stopUserLocationService()
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue