mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-01-19 05:45:15 +01:00
Merge pull request #7440 from BlueWallet/androidwi
FIX: Android widget fixes. Allow other currencies
This commit is contained in:
commit
636fc21f9c
@ -8,28 +8,55 @@ import androidx.work.WorkManager
|
||||
|
||||
class BitcoinPriceWidget : AppWidgetProvider() {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "BitcoinPriceWidget"
|
||||
private const val SHARED_PREF_NAME = "group.io.bluewallet.bluewallet"
|
||||
private const val WIDGET_COUNT_KEY = "widget_count"
|
||||
}
|
||||
|
||||
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
|
||||
super.onUpdate(context, appWidgetManager, appWidgetIds)
|
||||
Log.d("BitcoinPriceWidget", "onUpdate called")
|
||||
WidgetUpdateWorker.scheduleWork(context)
|
||||
for (widgetId in appWidgetIds) {
|
||||
Log.d(TAG, "Updating widget with ID: $widgetId")
|
||||
WidgetUpdateWorker.scheduleWork(context)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEnabled(context: Context) {
|
||||
super.onEnabled(context)
|
||||
Log.d("BitcoinPriceWidget", "onEnabled called")
|
||||
val sharedPref = context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
|
||||
val widgetCount = sharedPref.getInt(WIDGET_COUNT_KEY, 0)
|
||||
if (widgetCount >= 1) {
|
||||
Log.e(TAG, "Only one widget instance is allowed.")
|
||||
return
|
||||
}
|
||||
sharedPref.edit().putInt(WIDGET_COUNT_KEY, widgetCount + 1).apply()
|
||||
Log.d(TAG, "onEnabled called")
|
||||
WidgetUpdateWorker.scheduleWork(context)
|
||||
}
|
||||
|
||||
override fun onDisabled(context: Context) {
|
||||
super.onDisabled(context)
|
||||
Log.d("BitcoinPriceWidget", "onDisabled called")
|
||||
val sharedPref = context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
|
||||
val widgetCount = sharedPref.getInt(WIDGET_COUNT_KEY, 1)
|
||||
sharedPref.edit().putInt(WIDGET_COUNT_KEY, widgetCount - 1).apply()
|
||||
Log.d(TAG, "onDisabled called")
|
||||
clearCache(context)
|
||||
WorkManager.getInstance(context).cancelUniqueWork(WidgetUpdateWorker.WORK_NAME)
|
||||
}
|
||||
|
||||
private fun clearCache(context: Context) {
|
||||
val sharedPref = context.getSharedPreferences("group.io.bluewallet.bluewallet", Context.MODE_PRIVATE)
|
||||
sharedPref.edit().clear().apply() // Clear all preferences in the group
|
||||
Log.d("BitcoinPriceWidget", "Cache cleared from group.io.bluewallet.bluewallet")
|
||||
override fun onDeleted(context: Context, appWidgetIds: IntArray) {
|
||||
super.onDeleted(context, appWidgetIds)
|
||||
val sharedPref = context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
|
||||
val widgetCount = sharedPref.getInt(WIDGET_COUNT_KEY, 1)
|
||||
sharedPref.edit().putInt(WIDGET_COUNT_KEY, widgetCount - appWidgetIds.size).apply()
|
||||
Log.d(TAG, "onDeleted called for widgets: ${appWidgetIds.joinToString()}")
|
||||
}
|
||||
|
||||
private fun clearCache(context: Context) {
|
||||
val sharedPref = context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
|
||||
sharedPref.edit().clear().apply()
|
||||
Log.d(TAG, "Cache cleared from $SHARED_PREF_NAME")
|
||||
}
|
||||
|
||||
}
|
@ -2,6 +2,7 @@ package io.bluewallet.bluewallet
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import com.bugsnag.android.Bugsnag
|
||||
import com.facebook.react.PackageList
|
||||
import com.facebook.react.ReactApplication
|
||||
@ -16,6 +17,14 @@ import com.facebook.react.modules.i18nmanager.I18nUtil
|
||||
|
||||
class MainApplication : Application(), ReactApplication {
|
||||
|
||||
private lateinit var sharedPref: SharedPreferences
|
||||
private val preferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { prefs, key ->
|
||||
if (key == "preferredCurrency") {
|
||||
prefs.edit().remove("previous_price").apply()
|
||||
WidgetUpdateWorker.scheduleWork(this)
|
||||
}
|
||||
}
|
||||
|
||||
override val reactNativeHost: ReactNativeHost =
|
||||
object : DefaultReactNativeHost(this) {
|
||||
override fun getPackages(): List<ReactPackage> =
|
||||
@ -35,10 +44,10 @@ class MainApplication : Application(), ReactApplication {
|
||||
override val reactHost: ReactHost
|
||||
get() = getDefaultReactHost(applicationContext, reactNativeHost)
|
||||
|
||||
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
sharedPref = getSharedPreferences("group.io.bluewallet.bluewallet", Context.MODE_PRIVATE)
|
||||
sharedPref.registerOnSharedPreferenceChangeListener(preferenceChangeListener)
|
||||
val sharedI18nUtilInstance = I18nUtil.getInstance()
|
||||
sharedI18nUtilInstance.allowRTL(applicationContext, true)
|
||||
SoLoader.init(this, /* native exopackage */ false)
|
||||
@ -48,14 +57,17 @@ class MainApplication : Application(), ReactApplication {
|
||||
load()
|
||||
}
|
||||
|
||||
val sharedPref = applicationContext.getSharedPreferences("group.io.bluewallet.bluewallet", Context.MODE_PRIVATE)
|
||||
initializeBugsnag()
|
||||
}
|
||||
|
||||
// Retrieve the "donottrack" value. Default to "0" if not found.
|
||||
override fun onTerminate() {
|
||||
super.onTerminate()
|
||||
sharedPref.unregisterOnSharedPreferenceChangeListener(preferenceChangeListener)
|
||||
}
|
||||
|
||||
private fun initializeBugsnag() {
|
||||
val isDoNotTrackEnabled = sharedPref.getString("donottrack", "0")
|
||||
|
||||
// Check if do not track is not enabled and initialize Bugsnag if so
|
||||
if (isDoNotTrackEnabled != "1") {
|
||||
// Initialize Bugsnag or your error tracking here
|
||||
Bugsnag.start(this)
|
||||
}
|
||||
}
|
||||
|
@ -2,20 +2,21 @@ package io.bluewallet.bluewallet
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.json.JSONObject
|
||||
import java.io.InputStreamReader
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
|
||||
object MarketAPI {
|
||||
|
||||
private const val TAG = "MarketAPI"
|
||||
private val client = OkHttpClient()
|
||||
|
||||
var baseUrl: String? = null
|
||||
|
||||
fun fetchPrice(context: Context, currency: String): String? {
|
||||
suspend fun fetchPrice(context: Context, currency: String): String? {
|
||||
return try {
|
||||
// Load the JSON data from the assets
|
||||
val fiatUnitsJson = context.assets.open("fiatUnits.json").bufferedReader().use { it.readText() }
|
||||
val json = JSONObject(fiatUnitsJson)
|
||||
val currencyInfo = json.getJSONObject(currency)
|
||||
@ -25,26 +26,16 @@ object MarketAPI {
|
||||
val urlString = buildURLString(source, endPointKey)
|
||||
Log.d(TAG, "Fetching price from URL: $urlString")
|
||||
|
||||
val url = URL(urlString)
|
||||
val urlConnection = url.openConnection() as HttpURLConnection
|
||||
urlConnection.requestMethod = "GET"
|
||||
urlConnection.connect()
|
||||
val request = Request.Builder().url(urlString).build()
|
||||
val response = withContext(Dispatchers.IO) { client.newCall(request).execute() }
|
||||
|
||||
val responseCode = urlConnection.responseCode
|
||||
if (responseCode != 200) {
|
||||
Log.e(TAG, "Failed to fetch price. Response code: $responseCode")
|
||||
if (!response.isSuccessful) {
|
||||
Log.e(TAG, "Failed to fetch price. Response code: ${response.code}")
|
||||
return null
|
||||
}
|
||||
|
||||
val reader = InputStreamReader(urlConnection.inputStream)
|
||||
val jsonResponse = StringBuilder()
|
||||
val buffer = CharArray(1024)
|
||||
var read: Int
|
||||
while (reader.read(buffer).also { read = it } != -1) {
|
||||
jsonResponse.append(buffer, 0, read)
|
||||
}
|
||||
|
||||
parseJSONBasedOnSource(jsonResponse.toString(), source, endPointKey)
|
||||
val jsonResponse = response.body?.string() ?: return null
|
||||
parseJSONBasedOnSource(jsonResponse, source, endPointKey)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error fetching price", e)
|
||||
null
|
||||
|
@ -1,8 +1,10 @@
|
||||
package io.bluewallet.bluewallet
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
@ -13,8 +15,10 @@ import java.text.NumberFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class WidgetUpdateWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
|
||||
class WidgetUpdateWorker(context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) {
|
||||
|
||||
companion object {
|
||||
const val TAG = "WidgetUpdateWorker"
|
||||
@ -35,66 +39,57 @@ class WidgetUpdateWorker(context: Context, workerParams: WorkerParameters) : Wor
|
||||
}
|
||||
|
||||
private lateinit var sharedPref: SharedPreferences
|
||||
private lateinit var preferenceChangeListener: SharedPreferences.OnSharedPreferenceChangeListener
|
||||
|
||||
override fun doWork(): Result {
|
||||
override suspend fun doWork(): Result {
|
||||
Log.d(TAG, "Widget update worker running")
|
||||
|
||||
sharedPref = applicationContext.getSharedPreferences("group.io.bluewallet.bluewallet", Context.MODE_PRIVATE)
|
||||
registerPreferenceChangeListener()
|
||||
|
||||
val appWidgetManager = AppWidgetManager.getInstance(applicationContext)
|
||||
val thisWidget = ComponentName(applicationContext, BitcoinPriceWidget::class.java)
|
||||
val appWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget)
|
||||
val views = RemoteViews(applicationContext.packageName, R.layout.widget_layout)
|
||||
|
||||
val intent = Intent(applicationContext, MainActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
}
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
applicationContext,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
views.setOnClickPendingIntent(R.id.widget_layout, pendingIntent)
|
||||
|
||||
// Show loading indicator
|
||||
views.setViewVisibility(R.id.loading_indicator, View.VISIBLE)
|
||||
views.setViewVisibility(R.id.price_value, View.GONE)
|
||||
views.setViewVisibility(R.id.last_updated_label, View.GONE)
|
||||
views.setViewVisibility(R.id.last_updated_time, View.GONE)
|
||||
views.setViewVisibility(R.id.price_arrow_container, View.GONE)
|
||||
|
||||
appWidgetManager.updateAppWidget(appWidgetIds, views)
|
||||
|
||||
val preferredCurrency = sharedPref.getString("preferredCurrency", null) ?: "USD"
|
||||
val preferredCurrencyLocale = sharedPref.getString("preferredCurrencyLocale", null) ?: "en-US"
|
||||
val previousPrice = sharedPref.getString("previous_price", null)
|
||||
|
||||
val currentTime = SimpleDateFormat("hh:mm a", Locale.getDefault()).format(Date())
|
||||
|
||||
fetchPrice(preferredCurrency) { fetchedPrice, error ->
|
||||
handlePriceResult(
|
||||
appWidgetManager, appWidgetIds, views, sharedPref,
|
||||
fetchedPrice, previousPrice, currentTime, preferredCurrency, preferredCurrencyLocale, error
|
||||
)
|
||||
}
|
||||
val fetchedPrice = fetchPrice(preferredCurrency)
|
||||
|
||||
handlePriceResult(
|
||||
appWidgetManager, appWidgetIds, views, sharedPref,
|
||||
fetchedPrice, previousPrice, currentTime, preferredCurrency, preferredCurrencyLocale
|
||||
)
|
||||
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
private fun registerPreferenceChangeListener() {
|
||||
preferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
|
||||
if (key == "preferredCurrency" || key == "preferredCurrencyLocale" || key == "previous_price") {
|
||||
Log.d(TAG, "Preference changed: $key")
|
||||
updateWidgetOnPreferenceChange()
|
||||
}
|
||||
}
|
||||
sharedPref.registerOnSharedPreferenceChangeListener(preferenceChangeListener)
|
||||
}
|
||||
|
||||
override fun onStopped() {
|
||||
super.onStopped()
|
||||
sharedPref.unregisterOnSharedPreferenceChangeListener(preferenceChangeListener)
|
||||
}
|
||||
|
||||
private fun updateWidgetOnPreferenceChange() {
|
||||
val appWidgetManager = AppWidgetManager.getInstance(applicationContext)
|
||||
val thisWidget = ComponentName(applicationContext, BitcoinPriceWidget::class.java)
|
||||
val appWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget)
|
||||
val views = RemoteViews(applicationContext.packageName, R.layout.widget_layout)
|
||||
|
||||
val preferredCurrency = sharedPref.getString("preferredCurrency", null) ?: "USD"
|
||||
val preferredCurrencyLocale = sharedPref.getString("preferredCurrencyLocale", null) ?: "en-US"
|
||||
val previousPrice = sharedPref.getString("previous_price", null)
|
||||
val currentTime = SimpleDateFormat("hh:mm a", Locale.getDefault()).format(Date())
|
||||
|
||||
fetchPrice(preferredCurrency) { fetchedPrice, error ->
|
||||
handlePriceResult(
|
||||
appWidgetManager, appWidgetIds, views, sharedPref,
|
||||
fetchedPrice, previousPrice, currentTime, preferredCurrency, preferredCurrencyLocale, error
|
||||
)
|
||||
private suspend fun fetchPrice(currency: String?): String? {
|
||||
return withContext(Dispatchers.IO) {
|
||||
MarketAPI.fetchPrice(applicationContext, currency ?: "USD")
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,24 +102,27 @@ class WidgetUpdateWorker(context: Context, workerParams: WorkerParameters) : Wor
|
||||
previousPrice: String?,
|
||||
currentTime: String,
|
||||
preferredCurrency: String?,
|
||||
preferredCurrencyLocale: String?,
|
||||
error: String?
|
||||
preferredCurrencyLocale: String?
|
||||
) {
|
||||
val isPriceFetched = fetchedPrice != null
|
||||
val isPriceCached = previousPrice != null
|
||||
|
||||
if (error != null || !isPriceFetched) {
|
||||
Log.e(TAG, "Error fetching price: $error")
|
||||
if (!isPriceFetched) {
|
||||
Log.e(TAG, "Error fetching price.")
|
||||
if (!isPriceCached) {
|
||||
showLoadingError(views)
|
||||
} else {
|
||||
displayCachedPrice(views, previousPrice, currentTime, preferredCurrency, preferredCurrencyLocale)
|
||||
}
|
||||
} else {
|
||||
displayFetchedPrice(
|
||||
views, fetchedPrice!!, previousPrice, currentTime, preferredCurrency, preferredCurrencyLocale
|
||||
)
|
||||
savePrice(sharedPref, fetchedPrice)
|
||||
if (fetchedPrice != null) {
|
||||
displayFetchedPrice(
|
||||
views, fetchedPrice, previousPrice, currentTime, preferredCurrency, preferredCurrencyLocale
|
||||
)
|
||||
}
|
||||
if (fetchedPrice != null) {
|
||||
savePrice(sharedPref, fetchedPrice)
|
||||
}
|
||||
}
|
||||
|
||||
appWidgetManager.updateAppWidget(appWidgetIds, views)
|
||||
@ -132,7 +130,7 @@ class WidgetUpdateWorker(context: Context, workerParams: WorkerParameters) : Wor
|
||||
|
||||
private fun showLoadingError(views: RemoteViews) {
|
||||
views.apply {
|
||||
setViewVisibility(R.id.loading_indicator, View.VISIBLE)
|
||||
setViewVisibility(R.id.loading_indicator, View.GONE)
|
||||
setViewVisibility(R.id.price_value, View.GONE)
|
||||
setViewVisibility(R.id.last_updated_label, View.GONE)
|
||||
setViewVisibility(R.id.last_updated_time, View.GONE)
|
||||
@ -216,15 +214,6 @@ class WidgetUpdateWorker(context: Context, workerParams: WorkerParameters) : Wor
|
||||
return currencyFormat
|
||||
}
|
||||
|
||||
private fun fetchPrice(currency: String?, callback: (String?, String?) -> Unit) {
|
||||
val price = MarketAPI.fetchPrice(applicationContext, currency ?: "USD")
|
||||
if (price == null) {
|
||||
callback(null, "Failed to fetch price")
|
||||
} else {
|
||||
callback(price, null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun savePrice(sharedPref: SharedPreferences, price: String) {
|
||||
sharedPref.edit().putString("previous_price", price).apply()
|
||||
}
|
||||
|
@ -13,13 +13,14 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"/>
|
||||
android:visibility="visible"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="end">
|
||||
android:gravity="end"
|
||||
android:layout_gravity="end">
|
||||
<TextView
|
||||
android:id="@+id/last_updated_label"
|
||||
style="@style/WidgetTextSecondary"
|
||||
@ -29,69 +30,78 @@
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:visibility="gone"/>
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="end"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/last_updated_time"
|
||||
style="@style/WidgetTextPrimary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:text="--:--"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:visibility="gone"/>
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="end"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="end">
|
||||
android:gravity="end"
|
||||
android:layout_gravity="end">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/price_value"
|
||||
style="@style/WidgetTextPrimary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:visibility="gone"/>
|
||||
<LinearLayout
|
||||
android:id="@+id/price_arrow_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="end"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/price_arrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/previous_price_label"
|
||||
style="@style/WidgetTextPrimary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="From:"
|
||||
android:textSize="12sp"
|
||||
android:text="Loading..."
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
<TextView
|
||||
android:id="@+id/previous_price"
|
||||
style="@style/WidgetTextPrimary"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="end"/>
|
||||
<LinearLayout
|
||||
android:id="@+id/price_arrow_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textSize="12sp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
</LinearLayout></LinearLayout>
|
||||
android:orientation="horizontal"
|
||||
android:gravity="end"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="end">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/price_arrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_gravity="end"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/previous_price_label"
|
||||
style="@style/WidgetTextPrimary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="From:"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_gravity="end"/>
|
||||
<TextView
|
||||
android:id="@+id/previous_price"
|
||||
style="@style/WidgetTextPrimary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="--"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_gravity="end"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
Loading…
Reference in New Issue
Block a user