<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Daniel Ioni</title>
    <description>The latest articles on DEV Community by Daniel Ioni (@danielioni).</description>
    <link>https://dev.to/danielioni</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4014925%2Fcda2e740-8985-4918-92fc-3a8771453f44.png</url>
      <title>DEV Community: Daniel Ioni</title>
      <link>https://dev.to/danielioni</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/danielioni"/>
    <language>en</language>
    <item>
      <title>Building an Android App with Kotlin, Retrofit, and Coroutines: A Practical Guide2</title>
      <dc:creator>Daniel Ioni</dc:creator>
      <pubDate>Mon, 06 Jul 2026 06:05:03 +0000</pubDate>
      <link>https://dev.to/danielioni/building-an-android-app-with-kotlin-retrofit-and-coroutines-a-practical-guide2-14nm</link>
      <guid>https://dev.to/danielioni/building-an-android-app-with-kotlin-retrofit-and-coroutines-a-practical-guide2-14nm</guid>
      <description>&lt;h1&gt;
  
  
  Building an Android App with Kotlin, Retrofit, and Coroutines: A Practical Guide
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, I'll share my experience building a production-ready Android app for &lt;strong&gt;MyZubster&lt;/strong&gt;, an open-source skill exchange platform. I'll cover the key architectural decisions and code patterns I used.&lt;/p&gt;

&lt;p&gt;The app handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔐 JWT authentication with token management&lt;/li&gt;
&lt;li&gt;📅 Booking system with calendar&lt;/li&gt;
&lt;li&gt;💳 Monero payments with QR Code&lt;/li&gt;
&lt;li&gt;📋 Complete work history with pagination&lt;/li&gt;
&lt;li&gt;🔔 Real-time session management&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Tech Stack Overview
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Language&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Kotlin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Networking&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Retrofit + OkHttp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Async&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Coroutines + Flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Material Design + ViewBinding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SharedPreferences&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;QR Code&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ZXing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Architecture&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MVVM + Repository Pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  1. Authentication &amp;amp; Token Management
&lt;/h2&gt;

&lt;p&gt;I implemented JWT-based authentication with automatic token injection using &lt;code&gt;SharedPreferences&lt;/code&gt; for storage.&lt;/p&gt;

&lt;h3&gt;
  
  
  TokenManager.kt
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
kotlin
class TokenManager(context: Context) {
    private val prefs = context.getSharedPreferences("MyZubsterPrefs", Context.MODE_PRIVATE)

    fun saveAuthData(token: String, userId: String, email: String, name: String) {
        prefs.edit().apply {
            putString("jwt_token", token)
            putString("user_id", userId)
            putString("user_email", email)
            putString("user_name", name)
            apply()
        }
    }

    fun getToken(): String? = prefs.getString("jwt_token", null)
    fun getUserId(): String? = prefs.getString("user_id", null)
    fun isLoggedIn(): Boolean = !getToken().isNullOrEmpty()

    fun clear() {
        prefs.edit().clear().apply()
    }
}
LoginActivity.kt
kotlin

class LoginActivity : AppCompatActivity() {
    private lateinit var tokenManager: TokenManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)

        tokenManager = TokenManager(this)

        // Check if already logged in
        if (tokenManager.isLoggedIn()) {
            startActivity(Intent(this, MainActivity::class.java))
            finish()
        }
    }

    private fun performLogin(email: String, password: String) {
        // Simulate login (replace with actual API call)
        val token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
        tokenManager.saveAuthData(
            token = token,
            userId = "65f1a2b3c4d5e6f7g8h9i0j1",
            email = email,
            name = "Test User"
        )
        startActivity(Intent(this, MainActivity::class.java))
        finish()
    }
}

2. Networking with Retrofit

I used Retrofit with an AuthInterceptor to automatically add the JWT token to every request.
ApiService.kt
kotlin

interface ApiService {
    @POST("api/v1/auth/login")
    suspend fun login(
        @Body request: LoginRequest
    ): Response&amp;lt;AuthResponse&amp;gt;

    @GET("api/v1/bookings/history/{userId}")
    suspend fun getBookingHistory(
        @Path("userId") userId: String,
        @Query("page") page: Int = 1,
        @Query("limit") limit: Int = 10
    ): Response&amp;lt;BookingHistoryResponse&amp;gt;
}

AuthInterceptor.kt
kotlin

class AuthInterceptor(private val tokenManager: TokenManager) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val token = tokenManager.getToken()

        val newRequest = if (!token.isNullOrEmpty()) {
            request.newBuilder()
                .header("Authorization", "Bearer $token")
                .build()
        } else {
            request
        }

        return chain.proceed(newRequest)
    }
}

RetrofitClient.kt
kotlin

object RetrofitClient {
    private const val BASE_URL = "http://10.0.2.2:5000/"

    private val okHttpClient = OkHttpClient.Builder()
        .addInterceptor(AuthInterceptor(tokenManager))
        .connectTimeout(30, TimeUnit.SECONDS)
        .readTimeout(30, TimeUnit.SECONDS)
        .build()

    private val retrofit = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .client(okHttpClient)
        .addConverterFactory(GsonConverterFactory.create())
        .build()

    val apiService: ApiService by lazy {
        retrofit.create(ApiService::class.java)
    }
}

3. Booking History with Pagination

I implemented a RecyclerView with pagination using the Repository pattern and StateFlow.
BookingHistoryRepository.kt
kotlin

class BookingHistoryRepository {
    suspend fun getBookingHistory(
        userId: String,
        page: Int = 1,
        limit: Int = 10
    ): Response&amp;lt;BookingHistoryResponse&amp;gt; {
        return RetrofitClient.apiService.getBookingHistory(userId, page, limit)
    }
}

BookingHistoryViewModel.kt
kotlin

class BookingHistoryViewModel : ViewModel() {
    private val repository = BookingHistoryRepository()

    private val _bookings = MutableStateFlow&amp;lt;List&amp;lt;BookingHistory&amp;gt;&amp;gt;(emptyList())
    val bookings: StateFlow&amp;lt;List&amp;lt;BookingHistory&amp;gt;&amp;gt; = _bookings.asStateFlow()

    private val _isLoading = MutableStateFlow(false)
    val isLoading: StateFlow&amp;lt;Boolean&amp;gt; = _isLoading.asStateFlow()

    private val _error = MutableStateFlow&amp;lt;String?&amp;gt;(null)
    val error: StateFlow&amp;lt;String?&amp;gt; = _error.asStateFlow()

    private var currentPage = 1
    private var hasMoreData = true

    fun loadBookings(userId: String, page: Int = 1) {
        if (_isLoading.value) return

        viewModelScope.launch {
            _isLoading.value = true
            _error.value = null

            try {
                val response = repository.getBookingHistory(userId, page, 10)
                if (response.isSuccessful &amp;amp;&amp;amp; response.body()?.success == true) {
                    val data = response.body()?.data ?: emptyList()
                    if (page == 1) {
                        _bookings.value = data
                    } else {
                        _bookings.value = _bookings.value + data
                    }
                    hasMoreData = data.isNotEmpty()
                    currentPage = page
                } else {
                    _error.value = response.body()?.error ?: "Loading failed"
                }
            } catch (e: Exception) {
                _error.value = e.message
            } finally {
                _isLoading.value = false
            }
        }
    }

    fun loadNextPage() {
        if (hasMoreData &amp;amp;&amp;amp; !_isLoading.value) {
            loadBookings(currentPage + 1)
        }
    }
}

4. QR Code Generation

For Monero payments, I integrated QR code generation using the ZXing library.
PaymentActivity.kt
kotlin

class PaymentActivity : AppCompatActivity() {
    private lateinit var ivQRCode: ImageView
    private lateinit var tvAddress: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_payment)

        ivQRCode = findViewById(R.id.ivQRCode)
        tvAddress = findViewById(R.id.tvPaymentAddress)

        val address = "4A6d7f8g9h0i1j2k3l4m5n6o7p8q9r0s1t2u3v4w5x6y7z8"
        tvAddress.text = address
        ivQRCode.setImageBitmap(generateQRCode(address))
    }

    private fun generateQRCode(address: String): Bitmap {
        val writer = QRCodeWriter()
        val bitMatrix = writer.encode(address, BarcodeFormat.QR_CODE, 200, 200)
        val bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.RGB_565)

        for (x in 0 until 200) {
            for (y in 0 until 200) {
                bitmap.setPixel(x, y, if (bitMatrix[x, y]) Color.BLACK else Color.WHITE)
            }
        }
        return bitmap
    }
}

5. Session Management with Countdown

I implemented a session timer that shows remaining time in the UI.
SessionManager.kt
kotlin

class SessionManager(private val context: Context) {
    private val tokenManager = TokenManager(context)
    private val SESSION_DURATION = 3600L // 1 hour

    fun getSessionRemainingTime(): Long {
        // Calculate remaining time
        val loginTime = tokenManager.getLoginTimestamp()
        val elapsed = System.currentTimeMillis() - loginTime
        return (SESSION_DURATION * 1000 - elapsed) / 1000
    }

    fun getSessionCountdown(callback: (String) -&amp;gt; Unit) {
        Thread {
            while (true) {
                val remaining = getSessionRemainingTime()
                if (remaining &amp;lt;= 0) break

                val formatted = formatTime(remaining)
                Handler(Looper.getMainLooper()).post {
                    callback(formatted)
                }
                Thread.sleep(1000)
            }
        }.start()
    }

    private fun formatTime(seconds: Long): String {
        val hours = seconds / 3600
        val minutes = (seconds % 3600) / 60
        val secs = seconds % 60
        return "${hours}h ${minutes}m ${secs}s"
    }
}

6. Logout with Confirmation Dialog
kotlin

class MainActivity : AppCompatActivity() {
    private lateinit var tokenManager: TokenManager

    private fun logout() {
        AlertDialog.Builder(this)
            .setTitle("Logout")
            .setMessage("Are you sure you want to logout?")
            .setPositiveButton("Yes") { _, _ -&amp;gt;
                tokenManager.clear()
                startActivity(Intent(this, LoginActivity::class.java))
                finish()
            }
            .setNegativeButton("Cancel", null)
            .show()
    }
}

Key Takeaways

    Use Repository Pattern - Separates data sources from UI logic

    Implement AuthInterceptor - Automates token injection

    Use StateFlow - For reactive UI updates

    Handle Pagination - For efficient data loading

    Session Management - Improves user experience

Project Structure
text

app/src/main/java/com/myzubster/
├── activities/
│   ├── MainActivity.kt
│   └── BookingHistoryActivity.kt
├── adapters/
│   └── BookingHistoryAdapter.kt
├── models/
│   ├── BookingHistory.kt
│   └── AuthResponse.kt
├── network/
│   ├── ApiService.kt
│   ├── RetrofitClient.kt
│   └── AuthInterceptor.kt
├── repositories/
│   └── BookingHistoryRepository.kt
├── ui/auth/
│   ├── LoginActivity.kt
│   └── RegisterActivity.kt
├── utils/
│   ├── TokenManager.kt
│   └── SessionManager.kt
└── viewmodels/
    └── BookingHistoryViewModel.kt

Resources

The full project is open-source and available on GitHub:

🔗 DanielIoni-creator/MyZubsterAPP
💡 Want to Contribute?

    ⭐ Star the repository

    🐛 Report issues

    🔧 Submit pull requests

    💬 Share feedback

Follow me on GitHub: DanielIoni-creator

Tags: android kotlin retrofit coroutines opensource mvvm materialdesign
text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>android</category>
      <category>opensource</category>
      <category>monero</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>Building an Android App with Kotlin, Retrofit, and Coroutines: A Practical Guide</title>
      <dc:creator>Daniel Ioni</dc:creator>
      <pubDate>Mon, 06 Jul 2026 06:01:39 +0000</pubDate>
      <link>https://dev.to/danielioni/building-an-android-app-with-kotlin-retrofit-and-coroutines-a-practical-guide-24m1</link>
      <guid>https://dev.to/danielioni/building-an-android-app-with-kotlin-retrofit-and-coroutines-a-practical-guide-24m1</guid>
      <description>&lt;h1&gt;
  
  
  Building an Android App with Kotlin, Retrofit, and Coroutines: A Practical Guide
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, I'll share my experience building a production-ready Android app for &lt;strong&gt;MyZubster&lt;/strong&gt;, an open-source skill exchange platform. I'll cover the key architectural decisions and code patterns I used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech Stack Overview
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Language&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Kotlin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Networking&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Retrofit + OkHttp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Async&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Coroutines + Flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Material Design + ViewBinding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SharedPreferences (Token)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;QR Code&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ZXing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Architecture&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MVVM + Repository Pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  1. Authentication &amp;amp; Token Management
&lt;/h2&gt;

&lt;p&gt;I implemented JWT-based authentication with automatic token injection.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
kotlin
// TokenManager.kt
class TokenManager(context: Context) {
    private val prefs = context.getSharedPreferences("MyZubsterPrefs", Context.MODE_PRIVATE)

    fun saveAuthData(token: String, userId: String, email: String, name: String) {
        prefs.edit().apply {
            putString("jwt_token", token)
            putString("user_id", userId)
            putString("user_email", email)
            putString("user_name", name)
            apply()
        }
    }

    fun getToken(): String? = prefs.getString("jwt_token", null)
    fun isLoggedIn(): Boolean = !getToken().isNullOrEmpty()
}
2. Networking with Retrofit

I used Retrofit with an AuthInterceptor to automatically add the JWT token to requests.
kotlin

// AuthInterceptor.kt
class AuthInterceptor(private val tokenManager: TokenManager) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val token = tokenManager.getToken()

        val newRequest = if (!token.isNullOrEmpty()) {
            request.newBuilder()
                .header("Authorization", "Bearer $token")
                .build()
        } else {
            request
        }

        return chain.proceed(newRequest)
    }
}

3. Booking History with Pagination

I implemented a RecyclerView with pagination using the Repository pattern.
kotlin

// BookingHistoryViewModel.kt
class BookingHistoryViewModel : ViewModel() {
    private val _bookings = MutableStateFlow&amp;lt;List&amp;lt;BookingHistory&amp;gt;&amp;gt;(emptyList())
    val bookings: StateFlow&amp;lt;List&amp;lt;BookingHistory&amp;gt;&amp;gt; = _bookings.asStateFlow()

    private var currentPage = 1
    private var hasMoreData = true

    fun loadBookings(userId: String, page: Int = 1) {
        viewModelScope.launch {
            try {
                val response = repository.getBookingHistory(userId, page, 10)
                if (response.isSuccessful) {
                    _bookings.value = response.body()?.data ?: emptyList()
                    currentPage = response.body()?.pagination?.page ?: 1
                }
            } catch (e: Exception) {
                // Handle error
            }
        }
    }
}

4. QR Code Generation

For Monero payments, I integrated QR code generation for payment addresses.
kotlin

// PaymentActivity.kt
fun generateQRCode(address: String): Bitmap {
    val writer = QRCodeWriter()
    val bitMatrix = writer.encode(address, BarcodeFormat.QR_CODE, 200, 200)
    val bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.RGB_565)

    for (x in 0 until 200) {
        for (y in 0 until 200) {
            bitmap.setPixel(x, y, if (bitMatrix[x, y]) Color.BLACK else Color.WHITE)
        }
    }
    return bitmap
}

5. Session Management with Countdown

I implemented a session timer that shows remaining time in the UI.
kotlin

// SessionManager.kt
class SessionManager(private val context: Context) {
    fun getSessionCountdown(callback: (String) -&amp;gt; Unit) {
        Thread {
            while (isSessionValid()) {
                val remaining = tokenManager.getSessionRemainingTime()
                val formatted = formatTime(remaining)
                Handler(Looper.getMainLooper()).post {
                    callback(formatted)
                }
                sleep(1000)
            }
        }.start()
    }
}

Key Takeaways

    Use Repository Pattern - Separates data sources from UI logic

    Implement AuthInterceptor - Automates token injection

    Use StateFlow - For reactive UI updates

    Handle Pagination - For efficient data loading

    Session Management - Improves user experience

Resources

The full project is open-source and available on GitHub:

DanielIoni-creator/MyZubsterAPP

Follow me on GitHub: DanielIoni-creator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>blockchain</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Building MyZubster: An Open-Source Skill Exchange Platform with Monero Payments</title>
      <dc:creator>Daniel Ioni</dc:creator>
      <pubDate>Mon, 06 Jul 2026 05:32:58 +0000</pubDate>
      <link>https://dev.to/danielioni/building-myzubster-an-open-source-skill-exchange-platform-with-monero-payments-5dco</link>
      <guid>https://dev.to/danielioni/building-myzubster-an-open-source-skill-exchange-platform-with-monero-payments-5dco</guid>
      <description>&lt;h1&gt;
  
  
  🧩 Building MyZubster: An Open-Source Skill Exchange Platform with Monero Payments
&lt;/h1&gt;

&lt;h2&gt;
  
  
  📖 Introduction
&lt;/h2&gt;

&lt;p&gt;In a world where continuous learning is essential, &lt;strong&gt;MyZubster&lt;/strong&gt; is born as an open-source platform that connects people to exchange skills and services. From programming to plumbing, tutoring to tech support, MyZubster creates a community where everyone can teach what they know and learn what they desire.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/DanielIoni-creator/MyZubsterAPP" rel="noopener noreferrer"&gt;DanielIoni-creator/MyZubsterAPP&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 What Makes MyZubster Special?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🔐 Privacy &amp;amp; Monero Payments
&lt;/h3&gt;

&lt;p&gt;Unlike other platforms, MyZubster integrates &lt;strong&gt;Monero (XMR)&lt;/strong&gt; for private and secure payments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔒 No transaction tracking&lt;/li&gt;
&lt;li&gt;💰 Automatic 2% fee to support development&lt;/li&gt;
&lt;li&gt;🛡️ Escrow system: funds locked until work is completed&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🌐 SaaS &amp;amp; Self-Hosted Architecture
&lt;/h3&gt;

&lt;p&gt;MyZubster can be used as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SaaS&lt;/strong&gt; (Software as a Service) with automatic updates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-Hosted&lt;/strong&gt; for full control over your data&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🛠️ Tech Stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mobile&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Kotlin, Android SDK, Retrofit, Material Design&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Backend&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Node.js, Express, MongoDB, JWT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Payments&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Monero RPC, Escrow, 2% Fee Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Web Dashboard&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;React, TypeScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Admin Panel&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;React, Material-UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Gateway&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Node.js, Express&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ✨ Key Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  📅 Booking System
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Interactive calendar with time slots&lt;/li&gt;
&lt;li&gt;Status tracking: pending, confirmed, in_progress, completed, cancelled&lt;/li&gt;
&lt;li&gt;Automatic conflict detection&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  📝 Quotes &amp;amp; Estimates
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Professionals can send quotes&lt;/li&gt;
&lt;li&gt;Clients accept or reject&lt;/li&gt;
&lt;li&gt;Automatic booking status update&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  📋 Complete Work History
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Track all completed jobs&lt;/li&gt;
&lt;li&gt;Infinite scroll for loading more history&lt;/li&gt;
&lt;li&gt;Filter by category and status&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  💳 Monero Payment Integration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;2% platform fee automatically applied&lt;/li&gt;
&lt;li&gt;Escrow protection for both parties&lt;/li&gt;
&lt;li&gt;Privacy-first transactions&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📱 Android App Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🔐 Authentication
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Login/Register with email/password&lt;/li&gt;
&lt;li&gt;JWT token storage in SharedPreferences&lt;/li&gt;
&lt;li&gt;Session management with countdown timer&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  💰 Payment System
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;QR Code generation for Monero addresses&lt;/li&gt;
&lt;li&gt;Real-time payment status tracking&lt;/li&gt;
&lt;li&gt;Fund release by client after work completion&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  📊 Booking History
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;View all past and current bookings&lt;/li&gt;
&lt;li&gt;Status color coding&lt;/li&gt;
&lt;li&gt;Pull-to-refresh&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💰 Platform Fee
&lt;/h2&gt;

&lt;p&gt;MyZubster applies a &lt;strong&gt;2% platform fee&lt;/strong&gt; on all transactions to support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🖥️ Infrastructure costs (servers, databases, monitoring)&lt;/li&gt;
&lt;li&gt;👨‍💻 Development and maintenance&lt;/li&gt;
&lt;li&gt;🛡️ Security and escrow services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fee Wallet:&lt;/strong&gt; Maintained by Daniel Ioni (DanielIoni-creator)&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Getting Started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Android Studio (latest)&lt;/li&gt;
&lt;li&gt;Node.js 16+&lt;/li&gt;
&lt;li&gt;MongoDB&lt;/li&gt;
&lt;li&gt;Monero wallet RPC (for testing payments)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Clone the Repository
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
git clone https://github.com/DanielIoni-creator/MyZubsterAPP.git
cd MyZubsterAPP
Backend Setup
bash

cd backend
npm install
cp .env.example .env
# Edit .env with your MongoDB URI, Monero RPC URL, and API keys.
npm start
# The backend will run on http://localhost:5000

Android App
bash

# Open the project in Android Studio
# Sync Gradle and build the APK
# Install the APK on your device (or use an emulator)

Web Dashboard (Optional)
bash

cd web-dashboard
npm install
npm start
# Runs on http://localhost:3000

Admin Panel (Optional)
bash

cd admin-panel
npm install
npm start
# Runs on http://localhost:3001

🧪 Testing
bash

# Backend tests
cd backend
npm test

# Android tests
./gradlew test

# All tests with GitHub Actions (automated on every push)

🛡️ Security &amp;amp; Privacy

    Backend uses environment variables for sensitive data; never commit .env files

    All communication between client and server is encrypted via HTTPS

    Push notifications support Firebase FCM

    Admin Panel uses role-based access control (Admin/Moderator)

    Monero payments are non-custodial — private keys never leave the user's device

    Escrow uses Monero multisig for secure fund locking

📄 License

This project is licensed under either the MIT License or the GNU General Public License v3.0, at your option.

SPDX-License-Identifier: MIT OR GPL-3.0-or-later
🤝 How to Contribute

We welcome contributors of all experience levels!

    Fork the repository

    Create a feature branch

    Make your changes and test them

    Submit a Pull Request with a clear description of your work

Check our Contributing Guide to get started!
📊 Project Status
Component   Status
Backend API ✅ Complete
Android App ✅ Complete
Web Dashboard   ✅ Complete
Admin Panel ✅ Complete
Monero Integration  ✅ Complete
Authentication  ✅ Complete
Testing ✅ Complete
🔜 Next Steps

    Real Monero payment integration (currently in simulation)

    Push notifications with Firebase

    User profile management

    Multi-language support

    iOS app (Swift)

🙏 Acknowledgments

    Monero for privacy-first digital cash

    All open-source libraries and contributors who make this project possible

    The community for feedback and support

🚀 Ready to Join the Community?

Explore the code, report issues, or start contributing today!

https://img.shields.io/github/stars/DanielIoni-creator/MyZubsterAPP.svg?style=social
https://img.shields.io/github/forks/DanielIoni-creator/MyZubsterAPP.svg?style=social
https://img.shields.io/github/issues/DanielIoni-creator/MyZubsterAPP.svg
https://img.shields.io/github/issues-pr/DanielIoni-creator/MyZubsterAPP.svg

Made with ❤️ by Daniel Ioni and the MyZubster community

🔗 Links:

    GitHub Repository

    Report Issue

    Contributing Guide

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>android</category>
      <category>kotlin</category>
    </item>
  </channel>
</rss>
