Skip to content

Commit

Permalink
Exported RemoteViewService
Browse files Browse the repository at this point in the history
  • Loading branch information
svignesh93 committed Nov 28, 2021
1 parent 1a08b06 commit 27e7e20
Show file tree
Hide file tree
Showing 14 changed files with 194 additions and 101 deletions.
3 changes: 1 addition & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,8 @@ android {
}

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'androidx.appcompat:appcompat:1.4.0'
implementation 'com.google.android.material:material:1.4.0'

// Hilt
Expand Down
5 changes: 3 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
</receiver>

<receiver
android:name=".appwidget.CollectionWidgetProvider"
android:name=".appwidget.collection.CollectionWidgetProvider"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
Expand All @@ -75,7 +75,8 @@
</receiver>

<service
android:name=".appwidget.RemoteViewService"
android:name=".appwidget.collection.CollectionWidgetRVService"
android:exported="true"
android:permission="android.permission.BIND_REMOTEVIEWS" />

</application>
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/kotlin/com/litekite/sample/app/WidgetsApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject
import androidx.work.Configuration as WorkManagerConfig

/**
* Application class.
*
* @author Vignesh S
* @version 1.0, 07/11/2021
* @since 1.0
*/
@HiltAndroidApp
class WidgetsApp : Application(), WorkManagerConfig.Provider {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.litekite.sample.appwidget
package com.litekite.sample.appwidget.collection

/**
* @author Vignesh S
* @version 1.0, 04/11/2021
* @since 1.0
*/
data class CollectionWidgetItem(val text: String)
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.litekite.sample.appwidget
package com.litekite.sample.appwidget.collection

import android.app.PendingIntent
import android.appwidget.AppWidgetManager
Expand All @@ -22,17 +22,23 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.widget.RemoteViews
import android.widget.RemoteViewsService
import android.widget.Toast
import com.litekite.sample.R

/**
* @author Vignesh S
* @version 1.0, 28/11/2021
* @since 1.0
*/
class CollectionWidgetProvider : AppWidgetProvider() {

companion object {
private val TAG = CollectionWidgetProvider::class.java.simpleName
private const val TOAST_ACTION = "com.example.android.stackwidget.TOAST_ACTION"
const val EXTRA_ITEM = "com.example.android.stackwidget.EXTRA_ITEM"
}

// Called when the BroadcastReceiver receives an Intent broadcast.
Expand Down Expand Up @@ -60,7 +66,7 @@ class CollectionWidgetProvider : AppWidgetProvider() {
appWidgetIds.forEach { appWidgetId ->
// Set up the intent that starts the StackViewService, which will
// provide the views for this collection.
val intent = Intent(context, RemoteViewService::class.java).apply {
val intent = Intent(context, RemoteViewsService::class.java).apply {
// Add the app widget ID to the intent extras.
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME))
Expand Down Expand Up @@ -117,93 +123,3 @@ class CollectionWidgetProvider : AppWidgetProvider() {
}
}
}

class RemoteViewService : RemoteViewsService() {

override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
return StackRemoteViewsFactory(this.applicationContext, intent)
}
}

private const val TOAST_ACTION = "com.example.android.stackwidget.TOAST_ACTION"
private const val EXTRA_ITEM = "com.example.android.stackwidget.EXTRA_ITEM"
private const val REMOTE_VIEW_COUNT: Int = 10

class StackRemoteViewsFactory(
private val context: Context,
intent: Intent
) : RemoteViewsService.RemoteViewsFactory {

companion object {
private val TAG = StackRemoteViewsFactory::class.java.simpleName
}

private lateinit var collectionWidgetItems: MutableList<CollectionWidgetItem>
private val appWidgetId: Int = intent.getIntExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID
)

override fun onCreate() {
// In onCreate() you setup any connections / cursors to your data source. Heavy lifting,
// for example downloading or creating content etc, should be deferred to onDataSetChanged()
// or getViewAt(). Taking more than 20 seconds in this call will result in an ANR.
collectionWidgetItems = MutableList(REMOTE_VIEW_COUNT) { index ->
CollectionWidgetItem("$index!")
}
Log.d(TAG, "onCreate: appWidgetId: $appWidgetId")
}

/**
* Can do intense work
*/
override fun onDataSetChanged() {
// This is triggered when you call AppWidgetManager notifyAppWidgetViewDataChanged
// on the collection view corresponding to this factory. You can do heaving lifting in
// here, synchronously. For example, if you need to process an image, fetch something
// from the network, etc., it is ok to do it here, synchronously. The widget will remain
// in its current state while work is being done here, so you don't need to worry about
// locking up the widget.
}

override fun onDestroy() {
collectionWidgetItems.clear()
}

override fun getCount(): Int = collectionWidgetItems.size

/**
* Can do intense work
*/
override fun getViewAt(position: Int): RemoteViews {
// Construct a remote views item based on the app widget item XML file,
// and set the text based on the position.
return RemoteViews(context.packageName, R.layout.adapter_collection_widget_item).apply {
setTextViewText(R.id.widget_item, collectionWidgetItems[position].text)

// Next, set a fill-intent, which will be used to fill in the pending intent template
// that is set on the collection view in StackWidgetProvider.
val fillInIntent = Intent().apply {
Bundle().also { extras ->
extras.putInt(EXTRA_ITEM, position)
putExtras(extras)
}
}
// Make it possible to distinguish the individual on-click
// action of a given item
setOnClickFillInIntent(R.id.widget_item, fillInIntent)
}
}

override fun getLoadingView(): RemoteViews? {
// You can create a custom loading view (for instance when getViewAt() is slow.) If you
// return null here, you will get the default loading view.
return null
}

override fun getViewTypeCount(): Int = 1

override fun getItemId(position: Int): Long = position.toLong()

override fun hasStableIds(): Boolean = true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright 2021 LiteKite Startup. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.litekite.sample.appwidget.collection

import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.RemoteViews
import android.widget.RemoteViewsService
import com.litekite.sample.R

/**
* @author Vignesh S
* @version 1.0, 28/11/2021
* @since 1.0
*/
class CollectionWidgetRVFactory(
private val context: Context,
intent: Intent
) : RemoteViewsService.RemoteViewsFactory {

companion object {
private val TAG = CollectionWidgetRVFactory::class.java.simpleName
private const val REMOTE_VIEW_COUNT: Int = 10
}

private lateinit var collectionWidgetItems: MutableList<CollectionWidgetItem>
private val appWidgetId: Int = intent.getIntExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID
)

override fun onCreate() {
// In onCreate() you setup any connections / cursors to your data source. Heavy lifting,
// for example downloading or creating content etc, should be deferred to onDataSetChanged()
// or getViewAt(). Taking more than 20 seconds in this call will result in an ANR.
collectionWidgetItems = MutableList(REMOTE_VIEW_COUNT) { index ->
CollectionWidgetItem("$index!")
}
Log.d(TAG, "onCreate: appWidgetId: $appWidgetId")
}

/**
* Can do intense work
*/
override fun onDataSetChanged() {
// This is triggered when you call AppWidgetManager notifyAppWidgetViewDataChanged
// on the collection view corresponding to this factory. You can do heaving lifting in
// here, synchronously. For example, if you need to process an image, fetch something
// from the network, etc., it is ok to do it here, synchronously. The widget will remain
// in its current state while work is being done here, so you don't need to worry about
// locking up the widget.
}

override fun onDestroy() {
collectionWidgetItems.clear()
}

override fun getCount(): Int = collectionWidgetItems.size

/**
* Can do intense work
*/
override fun getViewAt(position: Int): RemoteViews {
// Construct a remote views item based on the app widget item XML file,
// and set the text based on the position.
return RemoteViews(context.packageName, R.layout.adapter_collection_widget_item).apply {
setTextViewText(R.id.widget_item, collectionWidgetItems[position].text)

// Next, set a fill-intent, which will be used to fill in the pending intent template
// that is set on the collection view in StackWidgetProvider.
val fillInIntent = Intent().apply {
Bundle().also { extras ->
extras.putInt(CollectionWidgetProvider.EXTRA_ITEM, position)
putExtras(extras)
}
}
// Make it possible to distinguish the individual on-click
// action of a given item
setOnClickFillInIntent(R.id.widget_item, fillInIntent)
}
}

override fun getLoadingView(): RemoteViews? {
// You can create a custom loading view (for instance when getViewAt() is slow.) If you
// return null here, you will get the default loading view.
return null
}

override fun getViewTypeCount(): Int = 1

override fun getItemId(position: Int): Long = position.toLong()

override fun hasStableIds(): Boolean = true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2021 LiteKite Startup. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.litekite.sample.appwidget.collection

import android.content.Intent
import android.widget.RemoteViewsService

/**
* @author Vignesh S
* @version 1.0, 04/11/2021
* @since 1.0
*/
class CollectionWidgetRVService : RemoteViewsService() {

override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
return CollectionWidgetRVFactory(this.applicationContext, intent)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ import com.litekite.sample.R
/**
* Configure the App Widget color, size, update period or other functionality settings
* when the widget is created.
*
* @author Vignesh S
* @version 1.0, 04/11/2021
* @since 1.0
*/
class AppWidgetConfigActivity : AppCompatActivity() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ import androidx.work.WorkManager
import com.litekite.sample.R
import com.litekite.sample.main.MainActivity

/**
* @author Vignesh S
* @version 1.0, 04/11/2021
* @since 1.0
*/
class WeatherAppWidgetProvider : AppWidgetProvider() {

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import java.util.concurrent.TimeUnit

/**
* @author Vignesh S
* @version 1.0, 07/11/2021
* @since 1.0
*/
object WeatherAppWidgetWorkScheduler {

fun scheduleOneShotWork(context: Context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ import dagger.assisted.AssistedInject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

/**
* @author Vignesh S
* @version 1.0, 07/11/2021
* @since 1.0
*/
@HiltWorker
class WeatherAppWidgetWorker @AssistedInject constructor(
@Assisted val context: Context,
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/kotlin/com/litekite/sample/main/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.litekite.sample.R

/**
* @author Vignesh S
* @version 1.0, 04/11/2021
* @since 1.0
*/
class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
Expand Down
Loading

0 comments on commit 27e7e20

Please sign in to comment.