Improve performances

This commit is contained in:
Tommaso Berlose 2021-01-08 17:49:12 +01:00
parent b903fff10f
commit c1d14f93bf
7 changed files with 119 additions and 139 deletions

View File

@ -48,7 +48,7 @@ import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeListener { class MainFragment : Fragment() {
companion object { companion object {
fun newInstance() = MainFragment() fun newInstance() = MainFragment()
@ -77,22 +77,16 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
// Init clock
if (Preferences.showClock) {
binding.widgetDetail.time.setTextColor(ColorHelper.getClockFontColor(requireActivity().isDarkTheme()))
binding.widgetDetail.time.setTextSize(TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(requireContext()))
binding.widgetDetail.timeAmPm.setTextColor(ColorHelper.getClockFontColor(requireActivity().isDarkTheme()))
binding.widgetDetail.timeAmPm.setTextSize(TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(requireContext()) / 5 * 2)
}
binding.widgetDetail.timeContainer.isVisible = Preferences.showClock
binding.preview.layoutParams = binding.preview.layoutParams.apply {
height = PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel(requireContext()) else 0
}
subscribeUi(viewModel) subscribeUi(viewModel)
if (binding.preview.height == 0) {
binding.preview.layoutParams = binding.preview.layoutParams.apply {
height = PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel(
requireContext()
) else 0
}
}
// Warnings // Warnings
if (getString(R.string.xiaomi_manufacturer).equals(Build.MANUFACTURER, ignoreCase = true) && Preferences.showXiaomiWarning) { if (getString(R.string.xiaomi_manufacturer).equals(Build.MANUFACTURER, ignoreCase = true) && Preferences.showXiaomiWarning) {
MaterialBottomSheetDialog(requireContext(), getString(R.string.xiaomi_warning_title), getString(R.string.xiaomi_warning_message)) MaterialBottomSheetDialog(requireContext(), getString(R.string.xiaomi_warning_title), getString(R.string.xiaomi_warning_message))
@ -130,12 +124,8 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
private var uiJob: Job? = null private var uiJob: Job? = null
private fun updateUI() { private fun updateUI() {
Log.d("ciao", "UPDATE UI")
uiJob?.cancel() uiJob?.cancel()
binding.preview.clearAnimation()
binding.widgetDetail.timeContainer.clearAnimation()
if (Preferences.showPreview) { if (Preferences.showPreview) {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
val bgColor: Int = ContextCompat.getColor( val bgColor: Int = ContextCompat.getColor(
@ -156,6 +146,7 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
binding.widgetDetail.widgetShapeBackground.setImageDrawable(wallpaperDrawable) binding.widgetDetail.widgetShapeBackground.setImageDrawable(wallpaperDrawable)
} }
} }
WidgetHelper.runWithCustomTypeface(requireContext()) { typeface -> WidgetHelper.runWithCustomTypeface(requireContext()) { typeface ->
uiJob = lifecycleScope.launch(Dispatchers.IO) { uiJob = lifecycleScope.launch(Dispatchers.IO) {
val generatedView = MainWidget.generateWidgetView(requireContext(), typeface).root val generatedView = MainWidget.generateWidgetView(requireContext(), typeface).root
@ -170,110 +161,18 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
if (binding.preview.width > 0) binding.preview.width else generatedView.measuredWidth, if (binding.preview.width > 0) binding.preview.width else generatedView.measuredWidth,
generatedView.measuredHeight generatedView.measuredHeight
) )
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
// Clock binding.widget.animate().alpha(0f).start()
binding.widgetDetail.time.setTextColor(ColorHelper.getClockFontColor(requireActivity().isDarkTheme())) binding.widgetLoader.animate().scaleX(1f).scaleY(1f).alpha(1f)
binding.widgetDetail.timeAmPm.setTextColor(ColorHelper.getClockFontColor(requireActivity().isDarkTheme()))
binding.widgetDetail.time.setTextSize(
TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(requireContext())
)
binding.widgetDetail.timeAmPm.setTextSize(
TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(requireContext()) / 5 * 2
)
binding.widgetDetail.timeAmPm.isVisible = Preferences.showAMPMIndicator
// Clock bottom margin .setDuration(200L).start()
binding.widgetDetail.clockBottomMarginNone.isVisible = binding.widgetDetail.bitmapContainer.apply {
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.NONE.value setImageBitmap(bitmap)
binding.widgetDetail.clockBottomMarginSmall.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.SMALL.value
binding.widgetDetail.clockBottomMarginMedium.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.MEDIUM.value
binding.widgetDetail.clockBottomMarginLarge.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.LARGE.value
if ((Preferences.showClock && (binding.widgetDetail.time.alpha < 1f)) || (!Preferences.showClock && (binding.widgetDetail.time.alpha > 0f))) {
if (Preferences.showClock) {
binding.widgetDetail.timeContainer.layoutParams = binding.widgetDetail.timeContainer.layoutParams.apply {
height = RelativeLayout.LayoutParams.WRAP_CONTENT
}
binding.widgetDetail.timeContainer.measure(0, 0)
}
val initialHeight = binding.widgetDetail.timeContainer.measuredHeight
ValueAnimator.ofFloat(
if (Preferences.showClock) 0f else 1f,
if (Preferences.showClock) 1f else 0f
).apply {
duration = 500L
addUpdateListener {
val animatedValue = animatedValue as Float
binding.widgetDetail.timeContainer.layoutParams =
binding.widgetDetail.timeContainer.layoutParams.apply {
height = (initialHeight * animatedValue).toInt()
}
binding.widgetDetail.time.alpha = animatedValue
}
addListener(
onStart = {
if (Preferences.showClock) {
binding.widgetDetail.timeContainer.isVisible = true
}
},
onEnd = {
if (!Preferences.showClock) {
binding.widgetDetail.timeContainer.isVisible = false
}
}
)
}.start()
ValueAnimator.ofInt(
binding.preview.height,
PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel(
requireContext()
) else 0
).apply {
duration = 500L
addUpdateListener {
val animatedValue = animatedValue as Int
val layoutParams = binding.preview.layoutParams
layoutParams.height = animatedValue
binding.preview.layoutParams = layoutParams
}
}.start()
} else {
binding.widgetDetail.timeContainer.layoutParams = binding.widgetDetail.timeContainer.layoutParams.apply {
height = RelativeLayout.LayoutParams.WRAP_CONTENT
}
binding.widgetDetail.timeContainer.measure(0, 0)
}
if (binding.preview.height == 0) {
ValueAnimator.ofInt(
binding.preview.height,
PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel(
requireContext()
) else 0
).apply {
duration = 300L
addUpdateListener {
val animatedValue = animatedValue as Int
val layoutParams = binding.preview.layoutParams
layoutParams.height = animatedValue
binding.preview.layoutParams = layoutParams
}
}.start()
} }
binding.widgetLoader.animate().scaleX(0f).scaleY(0f).alpha(0f) binding.widgetLoader.animate().scaleX(0f).scaleY(0f).alpha(0f)
.setDuration(200L).start() .setDuration(200L).start()
binding.widgetDetail.bitmapContainer.apply {
setImageBitmap(bitmap)
scaleX = 0.9f
scaleY = 0.9f
}
binding.widget.animate().alpha(1f).start() binding.widget.animate().alpha(1f).start()
} }
} }
@ -285,6 +184,43 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
} }
} }
private fun updateClock() {
// Clock
binding.widgetDetail.time.setTextColor(ColorHelper.getClockFontColor(requireActivity().isDarkTheme()))
binding.widgetDetail.timeAmPm.setTextColor(ColorHelper.getClockFontColor(requireActivity().isDarkTheme()))
binding.widgetDetail.time.setTextSize(
TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(requireContext())
)
binding.widgetDetail.timeAmPm.setTextSize(
TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(requireContext()) / 5 * 2
)
binding.widgetDetail.timeAmPm.isVisible = Preferences.showAMPMIndicator
// Clock bottom margin
binding.widgetDetail.clockBottomMarginNone.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.NONE.value
binding.widgetDetail.clockBottomMarginSmall.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.SMALL.value
binding.widgetDetail.clockBottomMarginMedium.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.MEDIUM.value
binding.widgetDetail.clockBottomMarginLarge.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.LARGE.value
if (Preferences.showClock) {
binding.widgetDetail.timeContainer.expand()
} else {
binding.widgetDetail.timeContainer.collapse()
}
binding.preview.layoutParams = binding.preview.layoutParams.apply {
height = PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel(
requireContext()
) else 0
}
}
private fun subscribeUi(viewModel: MainViewModel) { private fun subscribeUi(viewModel: MainViewModel) {
viewModel.showWallpaper.observe(viewLifecycleOwner) { viewModel.showWallpaper.observe(viewLifecycleOwner) {
if (it) { if (it) {
@ -328,39 +264,34 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
viewModel.fragmentScrollY.observe(viewLifecycleOwner) { viewModel.fragmentScrollY.observe(viewLifecycleOwner) {
binding.toolbar.cardElevation = if (it > 0) 24f else 0f binding.toolbar.cardElevation = if (it > 0) 24f else 0f
} }
viewModel.clockPreferencesUpdate.observe(viewLifecycleOwner) {
updateClock()
}
viewModel.widgetPreferencesUpdate.observe(viewLifecycleOwner) {
onUpdateUiEvent(null)
}
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
Preferences.preferences.registerOnSharedPreferenceChangeListener(this)
EventBus.getDefault().register(this) EventBus.getDefault().register(this)
updateUI() updateUI()
} }
override fun onPause() { override fun onPause() {
Preferences.preferences.unregisterOnSharedPreferenceChangeListener(this)
EventBus.getDefault().unregister(this) EventBus.getDefault().unregister(this)
super.onPause() super.onPause()
} }
private var delayJob: Job? = null private var delayJob: Job? = null
override fun onSharedPreferenceChanged(preferences: SharedPreferences, p1: String) {
delayJob?.cancel()
delayJob = lifecycleScope.launch(Dispatchers.IO) {
delay(300)
withContext(Dispatchers.Main) {
updateUI()
}
}
MainWidget.updateWidget(requireContext())
}
class UpdateUiMessageEvent class UpdateUiMessageEvent
class ChangeTabEvent(val page: Int) class ChangeTabEvent(val page: Int)
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(ignore: UpdateUiMessageEvent) { fun onUpdateUiEvent(ignore: UpdateUiMessageEvent?) {
delayJob?.cancel() delayJob?.cancel()
delayJob = lifecycleScope.launch(Dispatchers.IO) { delayJob = lifecycleScope.launch(Dispatchers.IO) {
delay(300) delay(300)

View File

@ -28,6 +28,7 @@ import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
import com.tommasoberlose.anotherwidget.helpers.IntentHelper import com.tommasoberlose.anotherwidget.helpers.IntentHelper
import com.tommasoberlose.anotherwidget.helpers.SettingsStringHelper import com.tommasoberlose.anotherwidget.helpers.SettingsStringHelper
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import com.tommasoberlose.anotherwidget.utils.isDefaultSet import com.tommasoberlose.anotherwidget.utils.isDefaultSet
import com.tommasoberlose.anotherwidget.utils.toast import com.tommasoberlose.anotherwidget.utils.toast
@ -185,7 +186,7 @@ class CalendarFragment : Fragment() {
binding.showAllDayToggle.setOnCheckedChangeListener { _, isChecked -> binding.showAllDayToggle.setOnCheckedChangeListener { _, isChecked ->
Preferences.calendarAllDay = isChecked Preferences.calendarAllDay = isChecked
updateCalendar() MainWidget.updateWidget(requireContext())
} }
binding.actionChangeAttendeeFilter.setOnClickListener { binding.actionChangeAttendeeFilter.setOnClickListener {
@ -230,7 +231,7 @@ class CalendarFragment : Fragment() {
binding.showOnlyBusyEventsToggle.setOnCheckedChangeListener { _, isChecked -> binding.showOnlyBusyEventsToggle.setOnCheckedChangeListener { _, isChecked ->
Preferences.showOnlyBusyEvents = isChecked Preferences.showOnlyBusyEvents = isChecked
updateCalendar() MainWidget.updateWidget(requireContext())
} }
binding.actionShowDiffTime.setOnClickListener { binding.actionShowDiffTime.setOnClickListener {

View File

@ -55,6 +55,7 @@ class MainViewModel : ViewModel() {
val openEventDetails = Preferences.asLiveData(Preferences::openEventDetails) val openEventDetails = Preferences.asLiveData(Preferences::openEventDetails)
val calendarAppName = Preferences.asLiveData(Preferences::calendarAppName) val calendarAppName = Preferences.asLiveData(Preferences::calendarAppName)
val widgetUpdateFrequency = Preferences.asLiveData(Preferences::widgetUpdateFrequency) val widgetUpdateFrequency = Preferences.asLiveData(Preferences::widgetUpdateFrequency)
val dateFormat = Preferences.asLiveData(Preferences::dateFormat)
// Clock Settings // Clock Settings
val showClock = Preferences.asLiveData(Preferences::showClock) val showClock = Preferences.asLiveData(Preferences::showClock)
@ -70,7 +71,6 @@ class MainViewModel : ViewModel() {
val showAMPMIndicator = Preferences.asLiveData(Preferences::showAMPMIndicator) val showAMPMIndicator = Preferences.asLiveData(Preferences::showAMPMIndicator)
val clockAppName = Preferences.asLiveData(Preferences::clockAppName) val clockAppName = Preferences.asLiveData(Preferences::clockAppName)
val dateFormat = Preferences.asLiveData(Preferences::dateFormat)
val clockBottomMargin = Preferences.asLiveData(Preferences::clockBottomMargin) val clockBottomMargin = Preferences.asLiveData(Preferences::clockBottomMargin)
// Weather Settings // Weather Settings
@ -95,4 +95,49 @@ class MainViewModel : ViewModel() {
// UI // UI
val fragmentScrollY = MutableLiveData<Int>() val fragmentScrollY = MutableLiveData<Int>()
val clockPreferencesUpdate = MediatorLiveData<Boolean>().apply {
addSource(Preferences.asLiveData(Preferences::showClock)) { value = true }
addSource(Preferences.asLiveData(Preferences::clockTextSize)) { value = true }
addSource(Preferences.asLiveData(Preferences::clockTextColor)) { value = true }
addSource(Preferences.asLiveData(Preferences::clockTextAlpha)) { value = true }
addSource(Preferences.asLiveData(Preferences::clockTextColorDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::clockTextAlphaDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::showAMPMIndicator)) { value = true }
addSource(Preferences.asLiveData(Preferences::clockBottomMargin)) { value = true }
}
val widgetPreferencesUpdate = MediatorLiveData<Boolean>().apply {
addSource(Preferences.asLiveData(Preferences::textGlobalColor)) { value = true }
addSource(Preferences.asLiveData(Preferences::textGlobalAlpha)) { value = true }
addSource(Preferences.asLiveData(Preferences::textSecondaryColor)) { value = true }
addSource(Preferences.asLiveData(Preferences::textSecondaryAlpha)) { value = true }
addSource(Preferences.asLiveData(Preferences::backgroundCardColor)) { value = true }
addSource(Preferences.asLiveData(Preferences::backgroundCardAlpha)) { value = true }
addSource(Preferences.asLiveData(Preferences::textGlobalColorDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::textGlobalAlphaDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::dateFormat)) { value = true }
addSource(Preferences.asLiveData(Preferences::textSecondaryColorDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::textSecondaryAlphaDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::backgroundCardColorDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::backgroundCardAlphaDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::textMainSize)) { value = true }
addSource(Preferences.asLiveData(Preferences::textSecondSize)) { value = true }
addSource(Preferences.asLiveData(Preferences::textShadow)) { value = true }
addSource(Preferences.asLiveData(Preferences::textShadowDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::customFont)) { value = true }
addSource(Preferences.asLiveData(Preferences::customFontFile)) { value = true }
addSource(Preferences.asLiveData(Preferences::customFontName)) { value = true }
addSource(Preferences.asLiveData(Preferences::customFontVariant)) { value = true }
addSource(Preferences.asLiveData(Preferences::secondRowInformation)) { value = true }
addSource(Preferences.asLiveData(Preferences::showDividers)) { value = true }
addSource(Preferences.asLiveData(Preferences::secondRowTopMargin)) { value = true }
addSource(Preferences.asLiveData(Preferences::showEvents)) { value = true }
addSource(Preferences.asLiveData(Preferences::calendarAllDay)) { value = true }
addSource(Preferences.asLiveData(Preferences::showDiffTime)) { value = true }
addSource(Preferences.asLiveData(Preferences::showNextEvent)) { value = true }
addSource(Preferences.asLiveData(Preferences::showWeather)) { value = true }
addSource(Preferences.asLiveData(Preferences::weatherTempUnit)) { value = true }
addSource(Preferences.asLiveData(Preferences::weatherIconPack)) { value = true }
addSource(Preferences.asLiveData(Preferences::showPreview)) { value = true }
}
} }

View File

@ -5,7 +5,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
android:background="?android:attr/colorPrimaryDark"
tools:context=".ui.activities.MainActivity"> tools:context=".ui.activities.MainActivity">
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -88,7 +87,8 @@
android:id="@+id/widget_bg" /> android:id="@+id/widget_bg" />
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="wrap_content"
android:animateLayoutChanges="true">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -97,6 +97,7 @@
android:orientation="vertical" android:orientation="vertical"
android:id="@+id/widget" android:id="@+id/widget"
android:alpha="0" android:alpha="0"
android:animateLayoutChanges="true"
android:gravity="center"> android:gravity="center">
<include layout="@layout/the_widget_sans" android:id="@+id/widget_detail" /> <include layout="@layout/the_widget_sans" android:id="@+id/widget_detail" />
</LinearLayout> </LinearLayout>

View File

@ -15,7 +15,8 @@
android:gravity="center" android:gravity="center"
android:paddingLeft="8dp" android:paddingLeft="8dp"
android:paddingRight="8dp" android:paddingRight="8dp"
android:id="@+id/main_layout"> android:id="@+id/main_layout"
android:animateLayoutChanges="true">
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -169,12 +169,12 @@
<!-- Clock --> <!-- Clock -->
<string name="settings_clock_title">Clock</string> <string name="settings_clock_title">Clock</string>
<string name="settings_clock_app_title">Tap on clock opens</string> <string name="settings_clock_app_title">Clock</string>
<string name="title_show_clock">Show Clock</string> <string name="title_show_clock">Show Clock</string>
<string name="show_clock_visible">Clock is visible</string> <string name="show_clock_visible">Clock is visible</string>
<string name="show_clock_not_visible">Clock is hidden</string> <string name="show_clock_not_visible">Clock is hidden</string>
<string name="settings_clock_text_size_title">Text size</string> <string name="settings_clock_text_size_title">Text size</string>
<string name="default_clock_app">Clock</string> <string name="default_clock_app">Default clock app</string>
<string name="settings_clock_bottom_margin_title">Clock bottom margin</string> <string name="settings_clock_bottom_margin_title">Clock bottom margin</string>
<string name="settings_clock_bottom_margin_subtitle_none">None</string> <string name="settings_clock_bottom_margin_subtitle_none">None</string>
<string name="settings_clock_bottom_margin_subtitle_small">Small</string> <string name="settings_clock_bottom_margin_subtitle_small">Small</string>

View File

@ -11,6 +11,7 @@
<item name="android:colorControlActivated">@color/colorAccent</item> <item name="android:colorControlActivated">@color/colorAccent</item>
<item name="colorControlHighlight">@color/black_20</item> <item name="colorControlHighlight">@color/black_20</item>
<item name="android:windowActivityTransitions">true</item> <item name="android:windowActivityTransitions">true</item>
<item name="android:windowShowWallpaper">true</item>
</style> </style>
<style name="AppTheme.Main" parent="AppTheme"> <style name="AppTheme.Main" parent="AppTheme">