nn-widgets 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,126 @@
1
+ package expo.modules.nnwidgets
2
+
3
+ import android.appwidget.AppWidgetManager
4
+ import android.content.ComponentName
5
+ import android.content.Context
6
+ import android.content.Intent
7
+ import android.content.SharedPreferences
8
+ import androidx.core.os.bundleOf
9
+ import expo.modules.kotlin.modules.Module
10
+ import expo.modules.kotlin.modules.ModuleDefinition
11
+ import expo.modules.kotlin.Promise
12
+ import org.json.JSONObject
13
+
14
+ class NNWidgetsModule : Module() {
15
+
16
+ companion object {
17
+ const val PREFS_NAME = "nn_widgets_data"
18
+ const val WIDGET_DATA_KEYS = "widget_data_keys"
19
+ }
20
+
21
+ private val context get() = requireNotNull(appContext.reactContext)
22
+
23
+ private fun getPreferences(): SharedPreferences {
24
+ return context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
25
+ }
26
+
27
+ override fun definition() = ModuleDefinition {
28
+ Name("NNWidgets")
29
+
30
+ // Check if widgets are supported (Android 4.0+, API 14+)
31
+ Function("isSupported") {
32
+ true // Widgets are supported on all modern Android versions
33
+ }
34
+
35
+ // Set widget data to SharedPreferences
36
+ AsyncFunction("setWidgetData") { data: Map<String, Any?>, promise: Promise ->
37
+ try {
38
+ val prefs = getPreferences()
39
+ val editor = prefs.edit()
40
+ val keys = mutableListOf<String>()
41
+
42
+ for ((key, value) in data) {
43
+ val prefKey = "widget_$key"
44
+ keys.add(prefKey)
45
+
46
+ when (value) {
47
+ is String -> editor.putString(prefKey, value)
48
+ is Int -> editor.putInt(prefKey, value)
49
+ is Long -> editor.putLong(prefKey, value)
50
+ is Float -> editor.putFloat(prefKey, value)
51
+ is Double -> editor.putFloat(prefKey, value.toFloat())
52
+ is Boolean -> editor.putBoolean(prefKey, value)
53
+ null -> editor.remove(prefKey)
54
+ else -> editor.putString(prefKey, value.toString())
55
+ }
56
+ }
57
+
58
+ // Store keys list
59
+ editor.putStringSet(WIDGET_DATA_KEYS, keys.toSet())
60
+ editor.apply()
61
+
62
+ println("[NNWidgets] Widget data saved: ${keys.joinToString(", ")}")
63
+ promise.resolve(true)
64
+ } catch (e: Exception) {
65
+ println("[NNWidgets] Failed to save widget data: ${e.message}")
66
+ promise.resolve(false)
67
+ }
68
+ }
69
+
70
+ // Get widget data from SharedPreferences
71
+ AsyncFunction("getWidgetData") { promise: Promise ->
72
+ try {
73
+ val prefs = getPreferences()
74
+ val keys = prefs.getStringSet(WIDGET_DATA_KEYS, emptySet()) ?: emptySet()
75
+ val result = mutableMapOf<String, Any?>()
76
+
77
+ for (key in keys) {
78
+ val cleanKey = key.removePrefix("widget_")
79
+ val value = prefs.all[key]
80
+ if (value != null) {
81
+ result[cleanKey] = value
82
+ }
83
+ }
84
+
85
+ promise.resolve(result)
86
+ } catch (e: Exception) {
87
+ println("[NNWidgets] Failed to get widget data: ${e.message}")
88
+ promise.resolve(emptyMap<String, Any?>())
89
+ }
90
+ }
91
+
92
+ // Request widget update
93
+ AsyncFunction("reloadWidget") { widgetClassName: String?, promise: Promise ->
94
+ try {
95
+ val appWidgetManager = AppWidgetManager.getInstance(context)
96
+
97
+ if (widgetClassName != null) {
98
+ // Update specific widget
99
+ val componentName = ComponentName(context.packageName, widgetClassName)
100
+ val widgetIds = appWidgetManager.getAppWidgetIds(componentName)
101
+
102
+ if (widgetIds.isNotEmpty()) {
103
+ val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE).apply {
104
+ putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIds)
105
+ component = componentName
106
+ }
107
+ context.sendBroadcast(intent)
108
+ println("[NNWidgets] Reloaded widget: $widgetClassName")
109
+ }
110
+ } else {
111
+ // Broadcast update to all widgets in the app
112
+ // This requires knowing the widget class names
113
+ // For now, we'll use a custom broadcast that widgets can listen to
114
+ val intent = Intent("${context.packageName}.WIDGET_UPDATE")
115
+ context.sendBroadcast(intent)
116
+ println("[NNWidgets] Sent update broadcast to all widgets")
117
+ }
118
+
119
+ promise.resolve(true)
120
+ } catch (e: Exception) {
121
+ println("[NNWidgets] Failed to reload widget: ${e.message}")
122
+ promise.resolve(false)
123
+ }
124
+ }
125
+ }
126
+ }
@@ -4,6 +4,7 @@
4
4
  "modules": ["NNWidgetsModule"]
5
5
  },
6
6
  "android": {
7
- "modules": ["expo.modules.nnwidgets.NNWidgetsModule"]
7
+ "modules": ["expo.modules.nnwidgets.NNWidgetsModule"],
8
+ "packageImportPath": "expo.modules.nnwidgets"
8
9
  }
9
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nn-widgets",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Expo config plugin for adding native widgets (iOS WidgetKit & Android App Widgets)",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -1 +1 @@
1
- {"version":3,"file":"withIosWidget.d.ts","sourceRoot":"","sources":["../src/withIosWidget.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAIb,MAAM,sBAAsB,CAAC;AAG9B,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EAEpB,MAAM,SAAS,CAAC;AA6mDjB,eAAO,MAAM,aAAa,EAAE,YAAY,CAAC,oBAAoB,CAugB5D,CAAC;AAMF,wBAAgB,eAAe,CAC7B,KAAK,EAAE,oBAAoB,EAC3B,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,MAAM,GACvB,mBAAmB,CA2GrB;AAED,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"withIosWidget.d.ts","sourceRoot":"","sources":["../src/withIosWidget.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAIb,MAAM,sBAAsB,CAAC;AAG9B,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EAEpB,MAAM,SAAS,CAAC;AA8mDjB,eAAO,MAAM,aAAa,EAAE,YAAY,CAAC,oBAAoB,CAugB5D,CAAC;AAMF,wBAAgB,eAAe,CAC7B,KAAK,EAAE,oBAAoB,EAC3B,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,MAAM,GACvB,mBAAmB,CA2GrB;AAED,eAAe,aAAa,CAAC"}
@@ -1044,11 +1044,10 @@ struct ${w.name}EntryView: View {
1044
1044
  .background(${bgColor})
1045
1045
  } else {
1046
1046
  GeometryReader { geometry in
1047
- let padding: CGFloat = 10
1048
- let availableWidth = geometry.size.width - (padding * 2)
1049
- let availableHeight = geometry.size.height - (padding * 2)
1047
+ let availableWidth = geometry.size.width
1048
+ let availableHeight = geometry.size.height
1050
1049
 
1051
- VStack(spacing: 8) {
1050
+ VStack(spacing: 6) {
1052
1051
  let layout = entry.layout
1053
1052
  let totalSlots = layout.map { $0.ratios.count }.reduce(0, +)
1054
1053
  let items = Array(entry.items.prefix(totalSlots))
@@ -1056,7 +1055,7 @@ struct ${w.name}EntryView: View {
1056
1055
  ForEach(0..<layout.count, id: \\.self) { rowIndex in
1057
1056
  let rowConfig = layout[rowIndex]
1058
1057
  let rowRatios = rowConfig.ratios
1059
- let rowHeight = (availableHeight - CGFloat(layout.count - 1) * 8) / CGFloat(layout.count)
1058
+ let rowHeight = (availableHeight - CGFloat(layout.count - 1) * 6) / CGFloat(layout.count)
1060
1059
 
1061
1060
  ${w.name}FlexRow(
1062
1061
  rowRatios: rowRatios,
@@ -1073,7 +1072,6 @@ struct ${w.name}EntryView: View {
1073
1072
  )
1074
1073
  }
1075
1074
  }
1076
- .padding(padding)
1077
1075
  }
1078
1076
  .background(${bgColor})
1079
1077
  }
@@ -1105,7 +1103,7 @@ struct ${w.name}FlexRow: View {
1105
1103
 
1106
1104
  var body: some View {
1107
1105
  let itemCount = rowRatios.count
1108
- let defaultSpacing: CGFloat = 8
1106
+ let defaultSpacing: CGFloat = 6
1109
1107
 
1110
1108
  // For space-* modes, scale down cells to leave room for visible spacing
1111
1109
  // Use 60% of width for content, 40% for spacing distribution
@@ -1228,7 +1226,6 @@ struct ${w.name}: Widget {
1228
1226
  .widgetURL(deepLinkUrl)
1229
1227
  } else {
1230
1228
  ${w.name}EntryView(entry: entry)
1231
- .padding()
1232
1229
  .background(${bgColor})
1233
1230
  .widgetURL(deepLinkUrl)
1234
1231
  }
@@ -1513,7 +1510,10 @@ extension Color {
1513
1510
  `
1514
1511
  : "";
1515
1512
  // Determine if we need UIKit import (for UIImage check in icon rendering)
1516
- const needsUIKit = props.widgets.some((w) => w.type === "list" || w.type === "single" || w.type === "flex-grid" || w.type === "grid");
1513
+ const needsUIKit = props.widgets.some((w) => w.type === "list" ||
1514
+ w.type === "single" ||
1515
+ w.type === "flex-grid" ||
1516
+ w.type === "grid");
1517
1517
  const uiKitImport = needsUIKit ? "\nimport UIKit" : "";
1518
1518
  return `//
1519
1519
  // Widgets.swift