Skip to content

Commit

Permalink
Merge pull request #1 from instana/INSTA-12431_adding_widget
Browse files Browse the repository at this point in the history
[INSTA-12431]Added widget to app
  • Loading branch information
jobykorahgeorgeibm authored and GitHub Enterprise committed Aug 11, 2024
2 parents b3140d2 + 8f0db15 commit 4727474
Show file tree
Hide file tree
Showing 8 changed files with 231 additions and 2 deletions.
2 changes: 2 additions & 0 deletions android-compose-robotshop/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ dependencies {
implementation(libs.retrofit.gson.converter)
implementation(libs.retrofit.core)
implementation(libs.coil.compose)
implementation(libs.glide)
implementation(libs.work.manager)
implementation(libs.accompanist.pager)
testImplementation(libs.junit)
implementation(libs.constraintlayout.compose)
Expand Down
26 changes: 24 additions & 2 deletions android-compose-robotshop/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<provider
android:name=".common.starttimehandler.StartupInitializer"
android:authorities="${applicationId}.appstartprovider"
android:initOrder="500"
android:initOrder="5000"
android:exported="false" />
<activity
android:name=".MainActivity"
Expand All @@ -31,7 +31,29 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<receiver android:name=".widget.RoboShopWidgetProvider"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_info" />
</receiver>
<!--Below code snippet is placed here to show that if you have any similar code in your manifest
Instana will throw error as Instana requires WorkManager to report beacons. If you uncomment below code,
error messages like "WorkManager is not initialized properly. You have explicitly disabled WorkManagerInitializer
in your manifest, have not manually called WorkManager"-->
<!-- <provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>-->
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.ibm.instashop.common.starttimehandler

import android.os.SystemClock
import android.util.Log
import com.ibm.instashop.MainActivity
import com.instana.android.CustomEvent
import com.instana.android.Instana

Expand All @@ -21,6 +22,7 @@ object LaunchTimeTracker{
}
launchTimeInNanos = SystemClock.elapsedRealtime() - this.initialTimeInNanos;
Instana.reportEvent(CustomEvent("APP_START_TIMINGS").apply {
viewName = MainActivity::class.java.toString()
duration = launchTimeInNanos
meta = mapOf(
"APP_START_LOAD_TIME_DURATION" to (launchTimeInNanos).toString()+"ms",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package com.ibm.instashop.widget

import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.graphics.Bitmap
import android.widget.RemoteViews
import androidx.work.Constraints
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import com.bumptech.glide.Glide
import com.google.gson.Gson
import com.ibm.instashop.R
import com.ibm.instashop.business_unit.models.ProductItem
import com.instana.android.core.event.worker.EventWorker
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.net.HttpURLConnection
import java.net.URL
import java.util.concurrent.TimeUnit
import kotlin.random.Random


class SharedPreferencesHelper(context: Context) {

private val sharedPreferences: SharedPreferences =
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)

companion object {
private const val PREFS_NAME = "robotshop_prefs"
private const val KEY_TITLE_STRING = "example_string_title"
const val KEY_URL_STRING = "example_string_title"
private const val KEY_EXAMPLE_BOOLEAN = "example_boolean"
}
fun saveString(value: String,key: String=KEY_TITLE_STRING) = sharedPreferences.edit().putString(key, value).apply()
fun getString(key: String = KEY_TITLE_STRING)= sharedPreferences.getString(KEY_TITLE_STRING, null)
fun saveBoolean(value: Boolean) = sharedPreferences.edit().putBoolean(KEY_EXAMPLE_BOOLEAN, value).apply()
// if you need to see flickering then you can uninstall the app and install again with below default value as true
fun getBoolean() = sharedPreferences.getBoolean(KEY_EXAMPLE_BOOLEAN, false)
}
class RoboShopWidgetProvider: AppWidgetProvider() {
private var bitMap:Bitmap? = null
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
super.onUpdate(context, appWidgetManager, appWidgetIds)
val prefs = SharedPreferencesHelper(context)
if (!prefs.getBoolean()) {
enqueueDelayedWorker(context)
prefs.saveBoolean(true)
}
val sharedPreferencesHelper = SharedPreferencesHelper(context)
val dataFromApi = sharedPreferencesHelper.getString() ?: "No data"
val urlFromData = sharedPreferencesHelper.getString(SharedPreferencesHelper.KEY_URL_STRING) ?: "http://www.google.com"
for (appWidgetId in appWidgetIds) {
val intent = Intent(context, javaClass).apply {
action = ACTION_FETCH_DATA
}
GlobalScope.launch {
bitMap = getBitMap(context,urlFromData)
}
val pendingIntent = PendingIntent.getBroadcast(
context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
val views = RemoteViews(context.packageName, R.layout.widget_layout).apply {
setOnClickPendingIntent(R.id.fetchDataBtn, pendingIntent)
setTextViewText(R.id.widget_text,dataFromApi)
setImageViewBitmap(R.id.image,bitMap)
}
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}


override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent)
val sharedPreferencesHelper = SharedPreferencesHelper(context)
if (ACTION_FETCH_DATA == intent.action) {
CoroutineScope(Dispatchers.IO).launch {
val result = fetchApiData()
sharedPreferencesHelper.saveString(result.title.take(17)+"...")
sharedPreferencesHelper.saveString(result.image,SharedPreferencesHelper.KEY_URL_STRING)
withContext(Dispatchers.Main) {
updateWidget(context, result)
}
}
}
}

private suspend fun fetchApiData(): ProductItem {
val url = URL("https://fakestoreapi.com/products/${Random.nextInt(1, 19)}") // Replace with your API endpoint
val connection = url.openConnection() as HttpURLConnection
return try {
val jsonResponse = connection.inputStream.bufferedReader().readText()
Gson().fromJson(jsonResponse, ProductItem::class.java)
} finally {
connection.disconnect()
}
}

private suspend fun updateWidget(context: Context, result: ProductItem) {
val appWidgetManager = AppWidgetManager.getInstance(context)
val componentName = ComponentName(context, RoboShopWidgetProvider::class.java)
val bitmap: Bitmap = withContext(Dispatchers.IO) {
Glide.with(context)
.asBitmap()
.load(result.image)
.submit(150, 100)
.get()
}
val views = RemoteViews(context.packageName, R.layout.widget_layout).apply {
setTextViewText(R.id.widget_text, result.title.take(17)+"..")
setImageViewBitmap(R.id.image,bitmap)
}

appWidgetManager.updateAppWidget(componentName, views)
}

companion object {
const val ACTION_FETCH_DATA = "com.ibm.instashop.widget.ACTION_FETCH_DATA"
}
//This is a workaround to overcome the issue with, WorkManager sending `android.appwidget.action.APPWIDGET_UPDATE`
//after the work is completed each time causing the Widget to update couple of times
//Issue Reference : https://issuetracker.google.com/issues/241076154
private fun enqueueDelayedWorker(context: Context) {
val workRequest = OneTimeWorkRequest.Builder(EventWorker::class.java)
.setInitialDelay(5 * 365, TimeUnit.DAYS)
.setConstraints(Constraints.Builder().setRequiresCharging(true).build())
.build()

WorkManager.getInstance(context).enqueueUniqueWork(
"appWidgetWorkerKeepEnabled",
ExistingWorkPolicy.KEEP,
workRequest
)
}


private suspend fun getBitMap(context: Context, url:String):Bitmap{
return withContext(Dispatchers.IO) {
Glide.with(context)
.asBitmap()
.load(url)
.submit(150, 100)
.get()
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="80dp" android:tint="#C125A77E"
android:viewportHeight="24" android:viewportWidth="24"
android:width="80dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:background="#FFFFFF">

<ImageView
android:id="@+id/image"
android:layout_width="150dp"
android:layout_height="99dp"
android:background="@color/white"
android:src="@drawable/widget_image"
android:scaleType="fitCenter"/>

<TextView
android:id="@+id/widget_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text=""
android:maxLines="1"
android:gravity="center"
android:textColor="#FF0000"
android:textSize="14sp"
android:textStyle="bold"/>

<Button
android:id="@+id/fetchDataBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:background="@color/teal_700"
android:text="Load Offers"/>
</LinearLayout>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/widget_layout"
android:minWidth="250dp"
android:minHeight="100dp"/>
4 changes: 4 additions & 0 deletions android-compose-robotshop/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,16 @@ material-android = "1.3.1"
compose-ui = "1.3.3"
navigation-compose = "2.5.3"
instana = "6.0.18"
glide = '4.16.0'
work-manager = "2.9.0"

[libraries]
#Instana Libraris
instana-runtime = { group = "com.instana", name = "android-agent-runtime", version.ref = "instana"} #instana group and name for vc
instana-plugin = { module = "com.instana:android-agent-plugin", version.ref = "instana"}

work-manager = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "work-manager"}
glide = { group = "com.github.bumptech.glide", name = "glide", version.ref = "glide"}
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" }
Expand Down

0 comments on commit 4727474

Please sign in to comment.