-
Notifications
You must be signed in to change notification settings - Fork 60
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Create glance sample app * Add hello world, action handling, error ui, stateful and list widgets * Add comments to the logic for reading the installed widgets info * Add widgets size mode examples * Center the content in the size mode examples * Add remote views interop examples * Update widgets metadata + Add docs for the hello world and action widgets * Add docs for remainder of widgets * Update widgets preview images * Update dependencies * Add screenshots * Create README.md
- Loading branch information
1 parent
29bd367
commit 7b84af4
Showing
68 changed files
with
1,803 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
*.iml | ||
.gradle | ||
/local.properties | ||
/.idea | ||
.DS_Store | ||
/build | ||
/captures | ||
.externalNativeBuild | ||
.cxx | ||
local.properties |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Glance - App widgets | ||
|
||
Android sample app to learn about the Jetpack Glance library, a recently introduced addition to Jetpack, built on top of compose that makes building app widgets easier. | ||
|
||
<img src="https://github.com/husaynhakeem/android-playground/blob/master/GlanceSample/screenshot/widgets-glance.png" alt="glance_widgets" height="1500"> | ||
|
||
The app showcases: | ||
- Building a basic app widget ([HelloWorldWidget](https://github.com/husaynhakeem/android-playground/blob/master/GlanceSample/app/src/main/java/com/husaynhakeem/glancesample/widget/HelloWorldWidget.kt)) | ||
- Handling user interactions by firing a callback, or launching an Activity, Service or BroadcastReceiver ([ActionWidget](https://github.com/husaynhakeem/android-playground/blob/master/GlanceSample/app/src/main/java/com/husaynhakeem/glancesample/widget/action/ActionWidget.kt)). | ||
- Handling widget errors and providing a custom error UI ([ErrorUIWidget](https://github.com/husaynhakeem/android-playground/blob/master/GlanceSample/app/src/main/java/com/husaynhakeem/glancesample/widget/ErrorUIWidget.kt)). | ||
- Composing the widget's UI using the UI components Glance offers, and using GlanceModifier to decorate them and define their behavior ([ListWidget](https://github.com/husaynhakeem/android-playground/blob/master/GlanceSample/app/src/main/java/com/husaynhakeem/glancesample/widget/ListWidget.kt)). | ||
- Building stateful app widgets, i.e storing the state of the widget's UI using a data store (e.g Preferences) and saving to it, as well as updating both the widget's state and refreshing its UI when the state changes ([StatefulWidget](https://github.com/husaynhakeem/android-playground/blob/master/GlanceSample/app/src/main/java/com/husaynhakeem/glancesample/widget/StatefulWidget.kt)). | ||
- Choosing the right size mode to define how the widget's UI reacts to the user resizing it. Glance provides 3 options to choose from: Single, Exact and Responsive ([SizeSingleWidget](https://github.com/husaynhakeem/android-playground/blob/master/GlanceSample/app/src/main/java/com/husaynhakeem/glancesample/widget/size/SizeSingleWidget.kt), [SizeExactWidget](https://github.com/husaynhakeem/android-playground/blob/master/GlanceSample/app/src/main/java/com/husaynhakeem/glancesample/widget/size/SizeExactWidget.kt), [SizeResponsiveWidget](https://github.com/husaynhakeem/android-playground/blob/master/GlanceSample/app/src/main/java/com/husaynhakeem/glancesample/widget/size/SizeResponsiveWidget.kt)). | ||
- Using Glance in interoperability with RemoteViews, the traditional way of building app widgets ([RemoteViewInteropWidget](https://github.com/husaynhakeem/android-playground/blob/master/GlanceSample/app/src/main/java/com/husaynhakeem/glancesample/widget/interop/RemoteViewInteropWidget.kt)). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
plugins { | ||
id 'com.android.application' | ||
id 'kotlin-android' | ||
} | ||
|
||
android { | ||
compileSdkVersion 31 | ||
defaultConfig { | ||
applicationId "com.husaynhakeem.glancesample" | ||
minSdkVersion 21 | ||
targetSdkVersion 31 | ||
} | ||
compileOptions { | ||
sourceCompatibility JavaVersion.VERSION_1_8 | ||
targetCompatibility JavaVersion.VERSION_1_8 | ||
} | ||
kotlinOptions { | ||
jvmTarget = '1.8' | ||
useIR = true | ||
} | ||
buildFeatures { | ||
compose true | ||
} | ||
composeOptions { | ||
kotlinCompilerExtensionVersion compose_version | ||
kotlinCompilerVersion kotlin_version | ||
} | ||
} | ||
|
||
dependencies { | ||
implementation 'androidx.core:core-ktx:1.7.0' | ||
implementation 'androidx.appcompat:appcompat:1.4.1' | ||
implementation 'com.google.android.material:material:1.5.0' | ||
implementation "androidx.compose.ui:ui:$compose_version" | ||
implementation "androidx.compose.material:material:$compose_version" | ||
implementation "androidx.compose.ui:ui-tooling:$compose_version" | ||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0' | ||
implementation 'androidx.activity:activity-compose:1.4.0' | ||
implementation "androidx.glance:glance-appwidget:1.0.0-alpha01" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
package="com.husaynhakeem.glancesample"> | ||
|
||
<application | ||
android:allowBackup="true" | ||
android:label="@string/app_name" | ||
android:supportsRtl="true" | ||
android:theme="@style/Theme.GlanceSample"> | ||
<activity | ||
android:name=".MainActivity" | ||
android:exported="true" | ||
android:label="@string/app_name" | ||
android:theme="@style/Theme.GlanceSample.NoActionBar"> | ||
<intent-filter> | ||
<action android:name="android.intent.action.MAIN" /> | ||
|
||
<category android:name="android.intent.category.LAUNCHER" /> | ||
</intent-filter> | ||
</activity> | ||
|
||
<activity android:name=".widget.action.DummyActivity" /> | ||
|
||
<service android:name=".widget.action.DummyService" /> | ||
|
||
<receiver android:name=".widget.action.DummyBroadcastReceiver" /> | ||
|
||
<!--Glance app widgets receivers--> | ||
<receiver | ||
android:name=".widget.HelloWorldWidgetReceiver" | ||
android:enabled="true" | ||
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_hello_world_info" /> | ||
|
||
</receiver> | ||
|
||
<receiver | ||
android:name=".widget.action.ActionWidgetReceiver" | ||
android:enabled="true" | ||
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_action_info" /> | ||
|
||
</receiver> | ||
|
||
<receiver | ||
android:name=".widget.ListWidgetReceiver" | ||
android:enabled="true" | ||
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_list_info" /> | ||
|
||
</receiver> | ||
|
||
<receiver | ||
android:name=".widget.ErrorUIWidgetReceiver" | ||
android:enabled="true" | ||
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_error_ui_info" /> | ||
|
||
</receiver> | ||
|
||
<receiver | ||
android:name=".widget.StatefulWidgetReceiver" | ||
android:enabled="true" | ||
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_stateful_info" /> | ||
|
||
</receiver> | ||
|
||
<!--Widget.size--> | ||
<receiver | ||
android:name=".widget.size.SizeExactWidgetReceiver" | ||
android:enabled="true" | ||
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_size_exact_info" /> | ||
|
||
</receiver> | ||
|
||
<receiver | ||
android:name=".widget.size.SizeResponsiveWidgetReceiver" | ||
android:enabled="true" | ||
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_size_responsive_info" /> | ||
|
||
</receiver> | ||
|
||
<receiver | ||
android:name=".widget.size.SizeSingleWidgetReceiver" | ||
android:enabled="true" | ||
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_size_single_info" /> | ||
|
||
</receiver> | ||
|
||
<!--Interop with remote views--> | ||
<receiver | ||
android:name=".widget.interop.RemoteViewWidgetProvider" | ||
android:enabled="true" | ||
android:exported="false" /> | ||
|
||
<receiver | ||
android:name=".widget.interop.RemoteViewInteropWidgetReceiver" | ||
android:enabled="true" | ||
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_interop_info" /> | ||
|
||
</receiver> | ||
</application> | ||
|
||
</manifest> |
48 changes: 48 additions & 0 deletions
48
GlanceSample/app/src/main/java/com/husaynhakeem/glancesample/MainActivity.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package com.husaynhakeem.glancesample | ||
|
||
import android.os.Bundle | ||
import androidx.activity.compose.setContent | ||
import androidx.activity.viewModels | ||
import androidx.appcompat.app.AppCompatActivity | ||
import androidx.compose.foundation.lazy.LazyColumn | ||
import androidx.compose.foundation.lazy.items | ||
import androidx.compose.material.MaterialTheme | ||
import androidx.compose.material.Surface | ||
import androidx.compose.material.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.lifecycle.lifecycleScope | ||
import com.husaynhakeem.glancesample.ui.theme.GlanceSampleTheme | ||
import kotlinx.coroutines.flow.launchIn | ||
import kotlinx.coroutines.flow.onEach | ||
|
||
class MainActivity : AppCompatActivity() { | ||
|
||
private val viewModel by viewModels<MainViewModel>() | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
viewModel | ||
.widgets | ||
.onEach { updateWidgets(it) } | ||
.launchIn(lifecycleScope) | ||
} | ||
|
||
private fun updateWidgets(widgets: List<Widget>) { | ||
setContent { | ||
GlanceSampleTheme { | ||
Surface(color = MaterialTheme.colors.background) { | ||
WidgetInfo(widgets = widgets) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
@Composable | ||
fun WidgetInfo(widgets: List<Widget>) { | ||
LazyColumn { | ||
items(widgets) { widget -> | ||
Text(text = widget.className) | ||
} | ||
} | ||
} |
66 changes: 66 additions & 0 deletions
66
GlanceSample/app/src/main/java/com/husaynhakeem/glancesample/MainViewModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package com.husaynhakeem.glancesample | ||
|
||
import android.app.Application | ||
import android.appwidget.AppWidgetManager | ||
import android.content.Context | ||
import androidx.compose.ui.unit.DpSize | ||
import androidx.glance.GlanceId | ||
import androidx.glance.appwidget.GlanceAppWidgetManager | ||
import androidx.glance.appwidget.GlanceAppWidgetReceiver | ||
import androidx.lifecycle.AndroidViewModel | ||
import androidx.lifecycle.viewModelScope | ||
import kotlinx.coroutines.flow.MutableStateFlow | ||
import kotlinx.coroutines.flow.asStateFlow | ||
import kotlinx.coroutines.launch | ||
|
||
class MainViewModel(application: Application) : AndroidViewModel(application) { | ||
|
||
private val _widgets = MutableStateFlow<List<Widget>>(emptyList()) | ||
val widgets = _widgets.asStateFlow() | ||
|
||
init { | ||
viewModelScope.launch { | ||
_widgets.emit(getAppWidgets(application)) | ||
} | ||
} | ||
|
||
@Suppress("ConvertCallChainIntoSequence") | ||
private suspend fun getAppWidgets(context: Context): List<Widget> { | ||
val manager = GlanceAppWidgetManager(context) | ||
return AppWidgetManager.getInstance(context) | ||
// Get all the installed widget providers | ||
.installedProviders | ||
// Filter only this app's widgets providers | ||
.filter { widgetProviderInfo -> widgetProviderInfo.provider.packageName == context.packageName } | ||
// Get this app's widget provider classes | ||
.mapNotNull { widgetProviderInfo -> Class.forName(widgetProviderInfo.provider.className) } | ||
// Filter only glance widget providers | ||
.filter { GlanceAppWidgetReceiver::class.java.isAssignableFrom(it) } | ||
// Get each widget provider's widget | ||
.map { (it.newInstance() as GlanceAppWidgetReceiver).glanceAppWidget.javaClass } | ||
// Convert each widget to a UI model | ||
.map { | ||
val metadata = manager.getGlanceIds(it) | ||
.map { id -> | ||
WidgetMetadata( | ||
id = id, | ||
sizes = manager.getAppWidgetSizes(id), | ||
) | ||
} | ||
Widget( | ||
className = it.simpleName, | ||
metadata = metadata, | ||
) | ||
} | ||
} | ||
} | ||
|
||
data class Widget( | ||
val className: String, | ||
val metadata: List<WidgetMetadata> | ||
) | ||
|
||
data class WidgetMetadata( | ||
val id: GlanceId, | ||
val sizes: List<DpSize> | ||
) |
8 changes: 8 additions & 0 deletions
8
GlanceSample/app/src/main/java/com/husaynhakeem/glancesample/ui/theme/Color.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.husaynhakeem.glancesample.ui.theme | ||
|
||
import androidx.compose.ui.graphics.Color | ||
|
||
val Purple200 = Color(0xFFBB86FC) | ||
val Purple500 = Color(0xFF6200EE) | ||
val Purple700 = Color(0xFF3700B3) | ||
val Teal200 = Color(0xFF03DAC5) |
11 changes: 11 additions & 0 deletions
11
GlanceSample/app/src/main/java/com/husaynhakeem/glancesample/ui/theme/Shape.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.husaynhakeem.glancesample.ui.theme | ||
|
||
import androidx.compose.foundation.shape.RoundedCornerShape | ||
import androidx.compose.material.Shapes | ||
import androidx.compose.ui.unit.dp | ||
|
||
val Shapes = Shapes( | ||
small = RoundedCornerShape(4.dp), | ||
medium = RoundedCornerShape(4.dp), | ||
large = RoundedCornerShape(0.dp) | ||
) |
Oops, something went wrong.