0

I have an AppWidget with a ListView. When using a dummy arrayList exampleData, the ListView show the data of it. But when I try to retrieve data from firebase RD to todayList, my ListView doesn't show the data. When I check the size of todayList it's not 0 which means the data successfully retrieved by todayList. Can you help me?

ListView when using todayList

enter image description here

ListView when using dummy exampleData

enter image description here

This is my ExampleWidgetItemFactory class:

internal class ExampleWidgetItemFactory(private val context: Context, intent: Intent) :
    RemoteViewsFactory {
    private lateinit var database: DatabaseReference
    private lateinit var preferences: Preferences
    private val mGoogleSignInClient: GoogleSignInClient? = null
    private lateinit var auth: FirebaseAuth
    private lateinit var user: FirebaseUser

    private lateinit var userId: String
    private lateinit var userName:String

    private var todayList: ArrayList<Task> = arrayListOf()
    private val appWidgetId: Int
    private val exampleData = arrayListOf(
        "one", "two", "three", "four",
        "five", "six", "seven", "eight", "nine", "ten"
    )

    override fun onCreate() {
        //connect to data source
        //Initialize
        database = FirebaseDatabase.getInstance().reference
        auth = Firebase.auth

        //User
        user = auth.currentUser
        userId = user.uid.toString()

        //Calendar
        val calendar = Calendar.getInstance()
        val year = calendar.get(Calendar.YEAR)
        val month = calendar.get(Calendar.MONTH) + 1
        val day = calendar.get(Calendar.DAY_OF_MONTH)
        val date = String.format("%02d/%02d/%04d", day, month, year)

        database
                .child("users")
                .child(userId)
                .child("tasks")
                .addValueEventListener(object : ValueEventListener {
                    override fun onDataChange(snapshot: DataSnapshot) {
                        //Hapus data dalam arraylist agar tidak jadi penumpukan data
                        todayList.clear()

                        //Ambil semua child dalam goal dan masukan ke items
                        var items = snapshot.children
                        //Lakukan iterasi pada setiap item lalu buat class dan tambahkan ke list
                        items.forEach {
                            var task = it.getValue(Task::class.java)
                            todayList.add(task!!)
                        }
                        Log.v("GAL", todayList.size.toString())
                        Log.v("GAL", todayList[0].title.toString())
                        SystemClock.sleep(3000)
                    }

                    override fun onCancelled(error: DatabaseError) {
                        TODO("Not yet implemented")
                    }

                })
    }

    override fun onDataSetChanged() {

    }
    override fun onDestroy() {
        //close data source
    }

    override fun getCount(): Int {
        return exampleData.size
    }

    override fun getViewAt(position: Int): RemoteViews {
        val views = RemoteViews(context.packageName, R.layout.example_widget_item)
        views.setTextViewText(R.id.example_widget_item_text, todayList[position].title)
        val fillIntent = Intent()
        fillIntent.putExtra(ExampleWidgetProvider().EXTRA_ITEM_POSITION, position)
        views.setOnClickFillInIntent(R.id.example_widget_item_text, fillIntent)
        SystemClock.sleep(500)
        return views
    }

    override fun getLoadingView(): RemoteViews? {
        return null
    }

    override fun getViewTypeCount(): Int {
        return 1
    }

    override fun getItemId(position: Int): Long {
        return position.toLong()
    }

    override fun hasStableIds(): Boolean {
        return true
    }

    init {
        appWidgetId = intent.getIntExtra(
            AppWidgetManager.EXTRA_APPWIDGET_ID,
            AppWidgetManager.INVALID_APPWIDGET_ID
        )
    }
}

**ExampleWidgetProvider **

class ExampleWidgetProvider : AppWidgetProvider() {
    val ACTION_TOAST = "actionToast"
    val EXTRA_ITEM_POSITION = "extraItemPosition"

    override fun onUpdate(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetIds: IntArray
    ) {
        super.onUpdate(context, appWidgetManager, appWidgetIds)
        for (appWidgetId in appWidgetIds) {
            val buttonIntent = Intent(context, HomeActivity::class.java)
            val buttonPendingIntent = PendingIntent.getActivity(
                context,
                0, buttonIntent, 0
            )
            val prefs = context.getSharedPreferences(
                ExampleAppWidgetConfig().SHARED_PREFS,
                Context.MODE_PRIVATE
            )
            val buttonText = prefs.getString(
                ExampleAppWidgetConfig().KEY_BUTTON_TEXT + appWidgetId,
                "Press me"
            )
            val serviceIntent = Intent(context, ExampleWidgetService::class.java)
            serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
            serviceIntent.data = Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME))

            val clickIntent =
                Intent(
                    context,ExampleWidgetProvider::class.java
                )
            clickIntent.action = ACTION_TOAST

            val clickPendingIntent = PendingIntent.getBroadcast(
                context,
                0, clickIntent, 0
            )

            val views = RemoteViews(context.packageName, R.layout.example_widget)
            views.setRemoteAdapter(R.id.example_widget_stack_view, serviceIntent)
            views.setEmptyView(R.id.example_widget_stack_view, R.id.example_widget_empty_view)
            views.setPendingIntentTemplate(R.id.example_widget_stack_view, clickPendingIntent)
            
            val appWidgetOptions = appWidgetManager.getAppWidgetOptions(appWidgetId)
            resizeWidget(appWidgetOptions, views)
            appWidgetManager.updateAppWidget(appWidgetId, views)
        }
    }

    override fun onAppWidgetOptionsChanged(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetId: Int,
        newOptions: Bundle
    ) {
        val views = RemoteViews(context.packageName, R.layout.example_widget)
        resizeWidget(newOptions, views)
        appWidgetManager.updateAppWidget(appWidgetId, views)
    }

    private fun resizeWidget(appWidgetOptions: Bundle, views: RemoteViews) {
        val minWidth = appWidgetOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)
        val maxWidth = appWidgetOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)
        val minHeight = appWidgetOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)
        val maxHeight = appWidgetOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)
    }

    override fun onDeleted(context: Context?, appWidgetIds: IntArray?) {
        Toast.makeText(context, "onDeleted", Toast.LENGTH_SHORT).show()
    }

    override fun onEnabled(context: Context?) {
        Toast.makeText(context, "onEnabled", Toast.LENGTH_SHORT).show()
    }

    override fun onDisabled(context: Context?) {
        Toast.makeText(context, "onDisabled", Toast.LENGTH_SHORT).show()
    }

    override fun onReceive(context: Context?, intent: Intent) {
        if (ACTION_TOAST == intent.action) {
            val clickedPosition = intent.getIntExtra(EXTRA_ITEM_POSITION, 0)
            Toast.makeText(context, "Clicked position: $clickedPosition", Toast.LENGTH_SHORT).show()
        }
        super.onReceive(context, intent)
    }

}

**When I try getting size and element in `todayList`:**

    Log.v("GAL", todayList.size.toString())
    Log.v("GAL", todayList[0].title.toString())

**The logcat result:**

    2021-04-15 11:57:41.554 8756-8756/com.giftech.taskmastertest V/GAL: 4
    2021-04-15 11:57:41.554 8756-8756/com.giftech.taskmastertest V/GAL: vv

galihif
  • 310
  • 1
  • 8
  • This **[answer](https://stackoverflow.com/questions/47847694/how-to-return-datasnapshot-value-as-a-result-of-a-method/47853774)** might help. – Alex Mamo Apr 15 '21 at 06:23

1 Answers1

1

why do you think that your items should be shown on list? you are fetching items from database, fulfiling todayList in forEach and... thats all. you are never returning this ArrayList anywhere, especially it isn't set to some adapter, which is attached to ListView. looks like you have stripped to much code, post whole class - I bet your problem is related to async database reading, onDataChange is called later than drawing adapter/ListView, on the end of this method you should probably call notifyDataSetChanged()

edit: after posting whole class now all is clear: RemoteViewsFactory is producing views basing on data passed to this class, similar work as adapters does. this isn't a moment for fetching data from network, as this is async operation, and creating RemoteViewsFactory is kind-of sync way for fetching views for already existing items. you should fetch your data inside extends AppWidgetProvider class (in onUpdate() method), there may be a need for starting some Service for async data fetching and after storing whole array refresh widget GUI

snachmsm
  • 17,866
  • 3
  • 32
  • 74
  • in method below i was returning the todayList, ok ill edit it – galihif Apr 15 '21 at 05:52
  • how i can call `notifyDataSetChanged()` ? i have no idea how to do it. Can you give me an example? – galihif Apr 15 '21 at 05:59
  • `notifyDataSetChanged` won't work in here sadly, app widget have own runtime and lifecycle, and `RemoteViewsFactory` is a class which is drawing your data, when it is created its too late for downloading data. check out my edit – snachmsm Apr 15 '21 at 06:10
  • oh okay, so all i have to do is redraw the data after the data is completely retrieved to list. But can u explain how to fetch inside the `onUpdate` method in `AppWidgetProvider ` ? hence it is on different class. I edit and add my `AppWidgetProvider ` – galihif Apr 15 '21 at 06:49
  • `AppWidgetProvider` extends `BroadcastReceiver`, so you can wake it up by sending broadcast to it, as you do with `ACTION_TOAST`. introduce new action e.g. `ACTION_NEW_DATA`, place your database fetching code inside `onUpdate` and on the end of `onDataChange` send `new Intent` with this action and whole array in extras. inside `onReceive` detect this action and call `onUpdate` passing array from extras. in [HERE](https://stackoverflow.com/questions/8304387/android-how-do-i-force-the-update-of-all-widgets-of-a-particular-kind) in answer (especially step 1) you have some sample – snachmsm Apr 15 '21 at 09:23