Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add requestPinWidget method #192

Merged
merged 15 commits into from
Jan 15, 2024
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,23 @@ WorkmanagerPlugin.setPluginRegistrantCallback { registry in
```
to [AppDelegate.swift](example/ios/Runner/AppDelegate.swift)

### Request Pin Widget
Requests to Pin (Add) the Widget to the users HomeScreen by pinning it to the users HomeScreen.

```dart
HomeWidget.requestPinWidget(
name: 'HomeWidgetExampleProvider',
androidName: 'HomeWidgetExampleProvider',
qualifiedAndroidName: 'com.example.app.HomeWidgetExampleProvider',
);
```

This method is only supported on [Android, API 26+](https://developer.android.com/develop/ui/views/appwidgets/configuration#pin).
If you want to check whether it is supported on current device, use:

```dart
HomeWidget.isRequestPinWidgetSupported();
```

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package es.antonborri.home_widget
import android.app.Activity
import android.appwidget.AppWidgetManager
import android.content.*
import android.os.Build
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
Expand Down Expand Up @@ -107,6 +108,37 @@ class HomeWidgetPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
saveCallbackHandle(context, dispatcher, callback)
return result.success(true)
}
"isRequestPinWidgetSupported" -> {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return result.success(false)
}

val appWidgetManager = AppWidgetManager.getInstance(context.applicationContext)
return result.success(appWidgetManager.isRequestPinAppWidgetSupported)
}
"requestPinWidget" -> {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return result.success(null)
}

val qualifiedName = call.argument<String>("qualifiedAndroidName")
val className = call.argument<String>("android") ?: call.argument<String>("name")

try {
val javaClass = Class.forName(qualifiedName ?: "${context.packageName}.${className}")
val myProvider = ComponentName(context, javaClass)

val appWidgetManager = AppWidgetManager.getInstance(context.applicationContext)

if (appWidgetManager.isRequestPinAppWidgetSupported) {
appWidgetManager.requestPinAppWidget(myProvider, null, null)
}

return result.success(null)
} catch (classException: ClassNotFoundException) {
result.error("-4", "No Widget found with Name $className. Argument 'name' must be the same as your AppWidgetProvider you wish to update", classException)
}
}
else -> {
result.notImplemented()
}
Expand Down
8 changes: 6 additions & 2 deletions ios/Classes/SwiftHomeWidgetPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,12 @@ public class SwiftHomeWidgetPlugin: NSObject, FlutterPlugin, FlutterStreamHandle
"Interactivity is only available on iOS 17.0",
details: nil))
}

} else {
} else if call.method == "isRequestPinWidgetSupported" {
result(false)
} else if call.method == "requestPinWidget" {
result(nil)
}
else {
result(FlutterMethodNotImplemented)
}
}
Expand Down
28 changes: 28 additions & 0 deletions lib/home_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,34 @@ class HomeWidget {
});
}

/// Determines whether pinning HomeScreen Widget is supported.
static Future<bool?> isRequestPinWidgetSupported() {
return _channel.invokeMethod('isRequestPinWidgetSupported');
}

/// Requests to Pin (Add) the HomeScreenWidget to the User's Home Screen
///
/// This is supported only on some Android Launchers and only with Android API 26+
///
/// Android Widgets will look for [qualifiedAndroidName] then [androidName] and then for [name]
/// There is no iOS alternative.
///
/// [qualifiedAndroidName] will use the name as is to find the WidgetProvider.
/// [androidName] must match the classname of the WidgetProvider, prefixed by the package name.
static Future<void> requestPinWidget({
String? name,
String? androidName,
// String? iOSName,
String? qualifiedAndroidName,
}) {
return _channel.invokeMethod('requestPinWidget', {
'name': name,
'android': androidName,
// 'ios': iOSName,
'qualifiedAndroidName': qualifiedAndroidName,
});
}

/// Returns Data saved with [saveWidgetData]
/// [id] of Data Saved
/// [defaultValue] value to use if no data was found
Expand Down
29 changes: 29 additions & 0 deletions test/home_widget_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ void main() {
return Future.value(launchUri);
case 'registerBackgroundCallback':
return true;
case 'requestPinWidget':
return null;
case 'isRequestPinWidgetSupported':
return true;
}
});
});
Expand Down Expand Up @@ -102,6 +106,31 @@ void main() {
expect(arguments['qualifiedAndroidName'], 'com.example.androidName');
});

test('isRequestPinWidgetSupported', () async {
expect(
await HomeWidget.isRequestPinWidgetSupported(),
true,
);

final arguments = await passedArguments.future;

expect(arguments, isNull);
});

test('requestPinWidget', () async {
await HomeWidget.requestPinWidget(
name: 'name',
androidName: 'androidName',
qualifiedAndroidName: 'com.example.androidName',
);

final arguments = await passedArguments.future;

expect(arguments['name'], 'name');
expect(arguments['android'], 'androidName');
expect(arguments['qualifiedAndroidName'], 'com.example.androidName');
});

group('initiallyLaunchedFromHomeWidget', () {
test('Valid Uri String gets parsed', () async {
launchUri = 'homeWidget://homeWidgetTest';
Expand Down
Loading