mirror of
https://github.com/flutter/packages.git
synced 2025-07-01 07:08:10 +08:00
[quick_actions] Convert android to pigeon (#5099)
Converts from direct use of method channels to using Pigeon. Part of https://github.com/flutter/flutter/issues/117844 part of https://github.com/flutter/flutter/issues/117913
This commit is contained in:
@ -1,3 +1,7 @@
|
|||||||
|
## 1.0.9
|
||||||
|
|
||||||
|
* Changes method channels to pigeon.
|
||||||
|
|
||||||
## 1.0.8
|
## 1.0.8
|
||||||
|
|
||||||
* Adds pub topics to package metadata.
|
* Adds pub topics to package metadata.
|
||||||
|
@ -0,0 +1,322 @@
|
|||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
// Autogenerated from Pigeon (v11.0.1), do not edit directly.
|
||||||
|
// See also: https://pub.dev/packages/pigeon
|
||||||
|
|
||||||
|
package io.flutter.plugins.quickactions;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import io.flutter.plugin.common.BasicMessageChannel;
|
||||||
|
import io.flutter.plugin.common.BinaryMessenger;
|
||||||
|
import io.flutter.plugin.common.MessageCodec;
|
||||||
|
import io.flutter.plugin.common.StandardMessageCodec;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** Generated class from Pigeon. */
|
||||||
|
@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression", "serial"})
|
||||||
|
public class Messages {
|
||||||
|
|
||||||
|
/** Error class for passing custom error details to Flutter via a thrown PlatformException. */
|
||||||
|
public static class FlutterError extends RuntimeException {
|
||||||
|
|
||||||
|
/** The error code. */
|
||||||
|
public final String code;
|
||||||
|
|
||||||
|
/** The error details. Must be a datatype supported by the api codec. */
|
||||||
|
public final Object details;
|
||||||
|
|
||||||
|
public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details) {
|
||||||
|
super(message);
|
||||||
|
this.code = code;
|
||||||
|
this.details = details;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
protected static ArrayList<Object> wrapError(@NonNull Throwable exception) {
|
||||||
|
ArrayList<Object> errorList = new ArrayList<Object>(3);
|
||||||
|
if (exception instanceof FlutterError) {
|
||||||
|
FlutterError error = (FlutterError) exception;
|
||||||
|
errorList.add(error.code);
|
||||||
|
errorList.add(error.getMessage());
|
||||||
|
errorList.add(error.details);
|
||||||
|
} else {
|
||||||
|
errorList.add(exception.toString());
|
||||||
|
errorList.add(exception.getClass().getSimpleName());
|
||||||
|
errorList.add(
|
||||||
|
"Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception));
|
||||||
|
}
|
||||||
|
return errorList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Home screen quick-action shortcut item.
|
||||||
|
*
|
||||||
|
* <p>Generated class from Pigeon that represents data sent in messages.
|
||||||
|
*/
|
||||||
|
public static final class ShortcutItemMessage {
|
||||||
|
/** The identifier of this item; should be unique within the app. */
|
||||||
|
private @NonNull String type;
|
||||||
|
|
||||||
|
public @NonNull String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(@NonNull String setterArg) {
|
||||||
|
if (setterArg == null) {
|
||||||
|
throw new IllegalStateException("Nonnull field \"type\" is null.");
|
||||||
|
}
|
||||||
|
this.type = setterArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Localized title of the item. */
|
||||||
|
private @NonNull String localizedTitle;
|
||||||
|
|
||||||
|
public @NonNull String getLocalizedTitle() {
|
||||||
|
return localizedTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocalizedTitle(@NonNull String setterArg) {
|
||||||
|
if (setterArg == null) {
|
||||||
|
throw new IllegalStateException("Nonnull field \"localizedTitle\" is null.");
|
||||||
|
}
|
||||||
|
this.localizedTitle = setterArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Name of native resource to be displayed as the icon for this item. */
|
||||||
|
private @Nullable String icon;
|
||||||
|
|
||||||
|
public @Nullable String getIcon() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIcon(@Nullable String setterArg) {
|
||||||
|
this.icon = setterArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructor is non-public to enforce null safety; use Builder. */
|
||||||
|
ShortcutItemMessage() {}
|
||||||
|
|
||||||
|
public static final class Builder {
|
||||||
|
|
||||||
|
private @Nullable String type;
|
||||||
|
|
||||||
|
public @NonNull Builder setType(@NonNull String setterArg) {
|
||||||
|
this.type = setterArg;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable String localizedTitle;
|
||||||
|
|
||||||
|
public @NonNull Builder setLocalizedTitle(@NonNull String setterArg) {
|
||||||
|
this.localizedTitle = setterArg;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable String icon;
|
||||||
|
|
||||||
|
public @NonNull Builder setIcon(@Nullable String setterArg) {
|
||||||
|
this.icon = setterArg;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull ShortcutItemMessage build() {
|
||||||
|
ShortcutItemMessage pigeonReturn = new ShortcutItemMessage();
|
||||||
|
pigeonReturn.setType(type);
|
||||||
|
pigeonReturn.setLocalizedTitle(localizedTitle);
|
||||||
|
pigeonReturn.setIcon(icon);
|
||||||
|
return pigeonReturn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
ArrayList<Object> toList() {
|
||||||
|
ArrayList<Object> toListResult = new ArrayList<Object>(3);
|
||||||
|
toListResult.add(type);
|
||||||
|
toListResult.add(localizedTitle);
|
||||||
|
toListResult.add(icon);
|
||||||
|
return toListResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
static @NonNull ShortcutItemMessage fromList(@NonNull ArrayList<Object> list) {
|
||||||
|
ShortcutItemMessage pigeonResult = new ShortcutItemMessage();
|
||||||
|
Object type = list.get(0);
|
||||||
|
pigeonResult.setType((String) type);
|
||||||
|
Object localizedTitle = list.get(1);
|
||||||
|
pigeonResult.setLocalizedTitle((String) localizedTitle);
|
||||||
|
Object icon = list.get(2);
|
||||||
|
pigeonResult.setIcon((String) icon);
|
||||||
|
return pigeonResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Result<T> {
|
||||||
|
@SuppressWarnings("UnknownNullness")
|
||||||
|
void success(T result);
|
||||||
|
|
||||||
|
void error(@NonNull Throwable error);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class AndroidQuickActionsApiCodec extends StandardMessageCodec {
|
||||||
|
public static final AndroidQuickActionsApiCodec INSTANCE = new AndroidQuickActionsApiCodec();
|
||||||
|
|
||||||
|
private AndroidQuickActionsApiCodec() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
|
||||||
|
switch (type) {
|
||||||
|
case (byte) 128:
|
||||||
|
return ShortcutItemMessage.fromList((ArrayList<Object>) readValue(buffer));
|
||||||
|
default:
|
||||||
|
return super.readValueOfType(type, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) {
|
||||||
|
if (value instanceof ShortcutItemMessage) {
|
||||||
|
stream.write(128);
|
||||||
|
writeValue(stream, ((ShortcutItemMessage) value).toList());
|
||||||
|
} else {
|
||||||
|
super.writeValue(stream, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||||
|
public interface AndroidQuickActionsApi {
|
||||||
|
/** Checks for, and returns the action that launched the app. */
|
||||||
|
@Nullable
|
||||||
|
String getLaunchAction();
|
||||||
|
/** Sets the dynamic shortcuts for the app. */
|
||||||
|
void setShortcutItems(
|
||||||
|
@NonNull List<ShortcutItemMessage> itemsList, @NonNull Result<Void> result);
|
||||||
|
/** Removes all dynamic shortcuts. */
|
||||||
|
void clearShortcutItems();
|
||||||
|
|
||||||
|
/** The codec used by AndroidQuickActionsApi. */
|
||||||
|
static @NonNull MessageCodec<Object> getCodec() {
|
||||||
|
return AndroidQuickActionsApiCodec.INSTANCE;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Sets up an instance of `AndroidQuickActionsApi` to handle messages through the
|
||||||
|
* `binaryMessenger`.
|
||||||
|
*/
|
||||||
|
static void setup(
|
||||||
|
@NonNull BinaryMessenger binaryMessenger, @Nullable AndroidQuickActionsApi api) {
|
||||||
|
{
|
||||||
|
BasicMessageChannel<Object> channel =
|
||||||
|
new BasicMessageChannel<>(
|
||||||
|
binaryMessenger,
|
||||||
|
"dev.flutter.pigeon.quick_actions_android.AndroidQuickActionsApi.getLaunchAction",
|
||||||
|
getCodec());
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler(
|
||||||
|
(message, reply) -> {
|
||||||
|
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||||
|
try {
|
||||||
|
String output = api.getLaunchAction();
|
||||||
|
wrapped.add(0, output);
|
||||||
|
} catch (Throwable exception) {
|
||||||
|
ArrayList<Object> wrappedError = wrapError(exception);
|
||||||
|
wrapped = wrappedError;
|
||||||
|
}
|
||||||
|
reply.reply(wrapped);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
BasicMessageChannel<Object> channel =
|
||||||
|
new BasicMessageChannel<>(
|
||||||
|
binaryMessenger,
|
||||||
|
"dev.flutter.pigeon.quick_actions_android.AndroidQuickActionsApi.setShortcutItems",
|
||||||
|
getCodec());
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler(
|
||||||
|
(message, reply) -> {
|
||||||
|
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||||
|
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||||
|
List<ShortcutItemMessage> itemsListArg = (List<ShortcutItemMessage>) args.get(0);
|
||||||
|
Result<Void> resultCallback =
|
||||||
|
new Result<Void>() {
|
||||||
|
public void success(Void result) {
|
||||||
|
wrapped.add(0, null);
|
||||||
|
reply.reply(wrapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void error(Throwable error) {
|
||||||
|
ArrayList<Object> wrappedError = wrapError(error);
|
||||||
|
reply.reply(wrappedError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
api.setShortcutItems(itemsListArg, resultCallback);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
BasicMessageChannel<Object> channel =
|
||||||
|
new BasicMessageChannel<>(
|
||||||
|
binaryMessenger,
|
||||||
|
"dev.flutter.pigeon.quick_actions_android.AndroidQuickActionsApi.clearShortcutItems",
|
||||||
|
getCodec());
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler(
|
||||||
|
(message, reply) -> {
|
||||||
|
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||||
|
try {
|
||||||
|
api.clearShortcutItems();
|
||||||
|
wrapped.add(0, null);
|
||||||
|
} catch (Throwable exception) {
|
||||||
|
ArrayList<Object> wrappedError = wrapError(exception);
|
||||||
|
wrapped = wrappedError;
|
||||||
|
}
|
||||||
|
reply.reply(wrapped);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
|
||||||
|
public static class AndroidQuickActionsFlutterApi {
|
||||||
|
private final @NonNull BinaryMessenger binaryMessenger;
|
||||||
|
|
||||||
|
public AndroidQuickActionsFlutterApi(@NonNull BinaryMessenger argBinaryMessenger) {
|
||||||
|
this.binaryMessenger = argBinaryMessenger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Public interface for sending reply. */
|
||||||
|
@SuppressWarnings("UnknownNullness")
|
||||||
|
public interface Reply<T> {
|
||||||
|
void reply(T reply);
|
||||||
|
}
|
||||||
|
/** The codec used by AndroidQuickActionsFlutterApi. */
|
||||||
|
static @NonNull MessageCodec<Object> getCodec() {
|
||||||
|
return new StandardMessageCodec();
|
||||||
|
}
|
||||||
|
/** Sends a string representing a shortcut from the native platform to the app. */
|
||||||
|
public void launchAction(@NonNull String actionArg, @NonNull Reply<Void> callback) {
|
||||||
|
BasicMessageChannel<Object> channel =
|
||||||
|
new BasicMessageChannel<>(
|
||||||
|
binaryMessenger,
|
||||||
|
"dev.flutter.pigeon.quick_actions_android.AndroidQuickActionsFlutterApi.launchAction",
|
||||||
|
getCodec());
|
||||||
|
channel.send(
|
||||||
|
new ArrayList<Object>(Collections.singletonList(actionArg)),
|
||||||
|
channelReply -> callback.reply(null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,180 +0,0 @@
|
|||||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
package io.flutter.plugins.quickactions;
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.ShortcutInfo;
|
|
||||||
import android.content.pm.ShortcutManager;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.drawable.Icon;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import io.flutter.plugin.common.MethodCall;
|
|
||||||
import io.flutter.plugin.common.MethodChannel;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler {
|
|
||||||
protected static final String EXTRA_ACTION = "some unique action key";
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
private Activity activity;
|
|
||||||
|
|
||||||
MethodCallHandlerImpl(Context context, Activity activity) {
|
|
||||||
this.context = context;
|
|
||||||
this.activity = activity;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setActivity(Activity activity) {
|
|
||||||
this.activity = activity;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) {
|
|
||||||
// We already know that this functionality does not work for anything
|
|
||||||
// lower than API 25 so we chose not to return error. Instead we do nothing.
|
|
||||||
result.success(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ShortcutManager shortcutManager =
|
|
||||||
(ShortcutManager) context.getSystemService(Context.SHORTCUT_SERVICE);
|
|
||||||
switch (call.method) {
|
|
||||||
case "setShortcutItems":
|
|
||||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {
|
|
||||||
List<Map<String, String>> serializedShortcuts = Objects.requireNonNull(call.arguments());
|
|
||||||
List<ShortcutInfo> shortcuts = deserializeShortcuts(serializedShortcuts);
|
|
||||||
|
|
||||||
Executor uiThreadExecutor = new UiThreadExecutor();
|
|
||||||
ThreadPoolExecutor executor =
|
|
||||||
new ThreadPoolExecutor(0, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
|
|
||||||
|
|
||||||
executor.execute(
|
|
||||||
() -> {
|
|
||||||
boolean dynamicShortcutsSet = false;
|
|
||||||
try {
|
|
||||||
shortcutManager.setDynamicShortcuts(shortcuts);
|
|
||||||
dynamicShortcutsSet = true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Leave dynamicShortcutsSet as false
|
|
||||||
}
|
|
||||||
|
|
||||||
final boolean didSucceed = dynamicShortcutsSet;
|
|
||||||
|
|
||||||
// TODO(camsim99): Move re-dispatch below to background thread when Flutter 2.8+ is
|
|
||||||
// stable.
|
|
||||||
uiThreadExecutor.execute(
|
|
||||||
() -> {
|
|
||||||
if (didSucceed) {
|
|
||||||
result.success(null);
|
|
||||||
} else {
|
|
||||||
result.error(
|
|
||||||
"quick_action_setshortcutitems_failure",
|
|
||||||
"Exception thrown when setting dynamic shortcuts",
|
|
||||||
null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
case "clearShortcutItems":
|
|
||||||
shortcutManager.removeAllDynamicShortcuts();
|
|
||||||
break;
|
|
||||||
case "getLaunchAction":
|
|
||||||
if (activity == null) {
|
|
||||||
result.error(
|
|
||||||
"quick_action_getlaunchaction_no_activity",
|
|
||||||
"There is no activity available when launching action",
|
|
||||||
null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final Intent intent = activity.getIntent();
|
|
||||||
final String launchAction = intent.getStringExtra(EXTRA_ACTION);
|
|
||||||
if (launchAction != null && !launchAction.isEmpty()) {
|
|
||||||
shortcutManager.reportShortcutUsed(launchAction);
|
|
||||||
intent.removeExtra(EXTRA_ACTION);
|
|
||||||
}
|
|
||||||
result.success(launchAction);
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
result.notImplemented();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
result.success(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.N_MR1)
|
|
||||||
private List<ShortcutInfo> deserializeShortcuts(List<Map<String, String>> shortcuts) {
|
|
||||||
final List<ShortcutInfo> shortcutInfos = new ArrayList<>();
|
|
||||||
|
|
||||||
for (Map<String, String> shortcut : shortcuts) {
|
|
||||||
final String icon = shortcut.get("icon");
|
|
||||||
final String type = shortcut.get("type");
|
|
||||||
final String title = shortcut.get("localizedTitle");
|
|
||||||
final ShortcutInfo.Builder shortcutBuilder = new ShortcutInfo.Builder(context, type);
|
|
||||||
|
|
||||||
final int resourceId = loadResourceId(context, icon);
|
|
||||||
final Intent intent = getIntentToOpenMainActivity(type);
|
|
||||||
|
|
||||||
if (resourceId > 0) {
|
|
||||||
shortcutBuilder.setIcon(Icon.createWithResource(context, resourceId));
|
|
||||||
}
|
|
||||||
|
|
||||||
final ShortcutInfo shortcutInfo =
|
|
||||||
shortcutBuilder.setLongLabel(title).setShortLabel(title).setIntent(intent).build();
|
|
||||||
shortcutInfos.add(shortcutInfo);
|
|
||||||
}
|
|
||||||
return shortcutInfos;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This method requires doing dynamic resource lookup, which is a discouraged API.
|
|
||||||
@SuppressWarnings("DiscouragedApi")
|
|
||||||
private int loadResourceId(Context context, String icon) {
|
|
||||||
if (icon == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
final String packageName = context.getPackageName();
|
|
||||||
final Resources res = context.getResources();
|
|
||||||
final int resourceId = res.getIdentifier(icon, "drawable", packageName);
|
|
||||||
|
|
||||||
if (resourceId == 0) {
|
|
||||||
return res.getIdentifier(icon, "mipmap", packageName);
|
|
||||||
} else {
|
|
||||||
return resourceId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Intent getIntentToOpenMainActivity(String type) {
|
|
||||||
final String packageName = context.getPackageName();
|
|
||||||
|
|
||||||
return context
|
|
||||||
.getPackageManager()
|
|
||||||
.getLaunchIntentForPackage(packageName)
|
|
||||||
.setAction(Intent.ACTION_RUN)
|
|
||||||
.putExtra(EXTRA_ACTION, type)
|
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
|
||||||
}
|
|
||||||
|
|
||||||
static class UiThreadExecutor implements Executor {
|
|
||||||
private final Handler handler = new Handler(Looper.getMainLooper());
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(Runnable command) {
|
|
||||||
handler.post(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,193 @@
|
|||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
package io.flutter.plugins.quickactions;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ShortcutInfo;
|
||||||
|
import android.content.pm.ShortcutManager;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.drawable.Icon;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import androidx.annotation.ChecksSdkIntAtLeast;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import io.flutter.plugins.quickactions.Messages.AndroidQuickActionsApi;
|
||||||
|
import io.flutter.plugins.quickactions.Messages.FlutterError;
|
||||||
|
import io.flutter.plugins.quickactions.Messages.Result;
|
||||||
|
import io.flutter.plugins.quickactions.Messages.ShortcutItemMessage;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
final class QuickActions implements AndroidQuickActionsApi {
|
||||||
|
protected static final String EXTRA_ACTION = "some unique action key";
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private Activity activity;
|
||||||
|
|
||||||
|
QuickActions(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setActivity(Activity activity) {
|
||||||
|
this.activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Activity getActivity() {
|
||||||
|
return this.activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true when running on a version of Android that supports quick actions.
|
||||||
|
// When this returns false, methods should silently no-op, per the documented behavior (see README.md).
|
||||||
|
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.N_MR1)
|
||||||
|
boolean isVersionAllowed() {
|
||||||
|
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setShortcutItems(
|
||||||
|
@NonNull List<ShortcutItemMessage> itemsList, @NonNull Result<Void> result) {
|
||||||
|
if (!isVersionAllowed()) {
|
||||||
|
result.success(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ShortcutManager shortcutManager =
|
||||||
|
(ShortcutManager) context.getSystemService(Context.SHORTCUT_SERVICE);
|
||||||
|
List<ShortcutInfo> shortcuts = shortcutItemMessageToShortcutInfo(itemsList);
|
||||||
|
Executor uiThreadExecutor = new UiThreadExecutor();
|
||||||
|
ThreadPoolExecutor executor =
|
||||||
|
new ThreadPoolExecutor(0, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
|
||||||
|
|
||||||
|
executor.execute(
|
||||||
|
() -> {
|
||||||
|
boolean dynamicShortcutsSet = false;
|
||||||
|
try {
|
||||||
|
shortcutManager.setDynamicShortcuts(shortcuts);
|
||||||
|
dynamicShortcutsSet = true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Leave dynamicShortcutsSet as false
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean didSucceed = dynamicShortcutsSet;
|
||||||
|
|
||||||
|
// TODO(camsim99): Investigate removing all of the executor logic in favor of background channels.
|
||||||
|
uiThreadExecutor.execute(
|
||||||
|
() -> {
|
||||||
|
if (didSucceed) {
|
||||||
|
result.success(null);
|
||||||
|
} else {
|
||||||
|
result.error(
|
||||||
|
new FlutterError(
|
||||||
|
"quick_action_setshortcutitems_failure",
|
||||||
|
"Exception thrown when setting dynamic shortcuts",
|
||||||
|
null));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearShortcutItems() {
|
||||||
|
if (!isVersionAllowed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ShortcutManager shortcutManager =
|
||||||
|
(ShortcutManager) context.getSystemService(Context.SHORTCUT_SERVICE);
|
||||||
|
shortcutManager.removeAllDynamicShortcuts();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String getLaunchAction() {
|
||||||
|
if (!isVersionAllowed()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ShortcutManager shortcutManager =
|
||||||
|
(ShortcutManager) context.getSystemService(Context.SHORTCUT_SERVICE);
|
||||||
|
if (activity == null) {
|
||||||
|
throw new FlutterError(
|
||||||
|
"quick_action_getlaunchaction_no_activity",
|
||||||
|
"There is no activity available when launching action",
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
final Intent intent = activity.getIntent();
|
||||||
|
final String launchAction = intent.getStringExtra(EXTRA_ACTION);
|
||||||
|
if (launchAction != null && !launchAction.isEmpty()) {
|
||||||
|
shortcutManager.reportShortcutUsed(launchAction);
|
||||||
|
intent.removeExtra(EXTRA_ACTION);
|
||||||
|
}
|
||||||
|
return launchAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.N_MR1)
|
||||||
|
private List<ShortcutInfo> shortcutItemMessageToShortcutInfo(
|
||||||
|
@NonNull List<ShortcutItemMessage> shortcuts) {
|
||||||
|
final List<ShortcutInfo> shortcutInfos = new ArrayList<>();
|
||||||
|
|
||||||
|
for (ShortcutItemMessage shortcut : shortcuts) {
|
||||||
|
final String icon = shortcut.getIcon();
|
||||||
|
final String type = shortcut.getType();
|
||||||
|
final String title = shortcut.getLocalizedTitle();
|
||||||
|
final ShortcutInfo.Builder shortcutBuilder = new ShortcutInfo.Builder(context, type);
|
||||||
|
|
||||||
|
final int resourceId = loadResourceId(context, icon);
|
||||||
|
final Intent intent = getIntentToOpenMainActivity(type);
|
||||||
|
|
||||||
|
if (resourceId > 0) {
|
||||||
|
shortcutBuilder.setIcon(Icon.createWithResource(context, resourceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
final ShortcutInfo shortcutInfo =
|
||||||
|
shortcutBuilder.setLongLabel(title).setShortLabel(title).setIntent(intent).build();
|
||||||
|
shortcutInfos.add(shortcutInfo);
|
||||||
|
}
|
||||||
|
return shortcutInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method requires doing dynamic resource lookup, which is a discouraged API.
|
||||||
|
@SuppressWarnings("DiscouragedApi")
|
||||||
|
private int loadResourceId(Context context, String icon) {
|
||||||
|
if (icon == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
final String packageName = context.getPackageName();
|
||||||
|
final Resources res = context.getResources();
|
||||||
|
final int resourceId = res.getIdentifier(icon, "drawable", packageName);
|
||||||
|
|
||||||
|
if (resourceId == 0) {
|
||||||
|
return res.getIdentifier(icon, "mipmap", packageName);
|
||||||
|
} else {
|
||||||
|
return resourceId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent getIntentToOpenMainActivity(String type) {
|
||||||
|
final String packageName = context.getPackageName();
|
||||||
|
|
||||||
|
return context
|
||||||
|
.getPackageManager()
|
||||||
|
.getLaunchIntentForPackage(packageName)
|
||||||
|
.setAction(Intent.ACTION_RUN)
|
||||||
|
.putExtra(EXTRA_ACTION, type)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class UiThreadExecutor implements Executor {
|
||||||
|
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Runnable command) {
|
||||||
|
handler.post(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,23 +9,22 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ShortcutManager;
|
import android.content.pm.ShortcutManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
import androidx.annotation.ChecksSdkIntAtLeast;
|
import androidx.annotation.ChecksSdkIntAtLeast;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
||||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
|
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
|
||||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
|
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
|
||||||
import io.flutter.plugin.common.BinaryMessenger;
|
|
||||||
import io.flutter.plugin.common.MethodChannel;
|
|
||||||
import io.flutter.plugin.common.PluginRegistry.NewIntentListener;
|
import io.flutter.plugin.common.PluginRegistry.NewIntentListener;
|
||||||
|
import io.flutter.plugins.quickactions.Messages.AndroidQuickActionsFlutterApi;
|
||||||
|
|
||||||
/** QuickActionsPlugin */
|
/** QuickActionsPlugin */
|
||||||
public class QuickActionsPlugin implements FlutterPlugin, ActivityAware, NewIntentListener {
|
public class QuickActionsPlugin implements FlutterPlugin, ActivityAware, NewIntentListener {
|
||||||
private static final String CHANNEL_ID = "plugins.flutter.io/quick_actions_android";
|
private static final String TAG = "QuickActionsAndroid";
|
||||||
|
|
||||||
private MethodChannel channel;
|
private QuickActions quickActions;
|
||||||
private MethodCallHandlerImpl handler;
|
private AndroidQuickActionsFlutterApi quickActionsFlutterApi;
|
||||||
private Activity activity;
|
|
||||||
private final @NonNull AndroidSdkChecker sdkChecker;
|
private final @NonNull AndroidSdkChecker sdkChecker;
|
||||||
|
|
||||||
// Interface for an injectable SDK version checker.
|
// Interface for an injectable SDK version checker.
|
||||||
@ -52,31 +51,40 @@ public class QuickActionsPlugin implements FlutterPlugin, ActivityAware, NewInte
|
|||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public static void registerWith(
|
public static void registerWith(
|
||||||
@NonNull io.flutter.plugin.common.PluginRegistry.Registrar registrar) {
|
@NonNull io.flutter.plugin.common.PluginRegistry.Registrar registrar) {
|
||||||
final QuickActionsPlugin plugin = new QuickActionsPlugin();
|
QuickActions quickActions = new QuickActions(registrar.context());
|
||||||
plugin.setupChannel(registrar.messenger(), registrar.context(), registrar.activity());
|
quickActions.setActivity(registrar.activity());
|
||||||
|
Messages.AndroidQuickActionsApi.setup(registrar.messenger(), quickActions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
|
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
|
||||||
setupChannel(binding.getBinaryMessenger(), binding.getApplicationContext(), null);
|
this.quickActions = new QuickActions(binding.getApplicationContext());
|
||||||
|
Messages.AndroidQuickActionsApi.setup(binding.getBinaryMessenger(), quickActions);
|
||||||
|
this.quickActionsFlutterApi = new AndroidQuickActionsFlutterApi(binding.getBinaryMessenger());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
|
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
|
||||||
teardownChannel();
|
Messages.AndroidQuickActionsApi.setup(binding.getBinaryMessenger(), null);
|
||||||
|
this.quickActions = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
|
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
|
||||||
activity = binding.getActivity();
|
if (this.quickActions == null) {
|
||||||
handler.setActivity(activity);
|
Log.wtf(TAG, "quickActions was never set.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Activity activity = binding.getActivity();
|
||||||
|
this.quickActions.setActivity(activity);
|
||||||
binding.addOnNewIntentListener(this);
|
binding.addOnNewIntentListener(this);
|
||||||
onNewIntent(activity.getIntent());
|
onNewIntent(activity.getIntent());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDetachedFromActivity() {
|
public void onDetachedFromActivity() {
|
||||||
handler.setActivity(null);
|
quickActions.setActivity(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -96,27 +104,20 @@ public class QuickActionsPlugin implements FlutterPlugin, ActivityAware, NewInte
|
|||||||
if (!sdkChecker.sdkIsAtLeast(Build.VERSION_CODES.N_MR1)) {
|
if (!sdkChecker.sdkIsAtLeast(Build.VERSION_CODES.N_MR1)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Activity activity = this.quickActions.getActivity();
|
||||||
// Notify the Dart side if the launch intent has the intent extra relevant to quick actions.
|
// Notify the Dart side if the launch intent has the intent extra relevant to quick actions.
|
||||||
if (intent.hasExtra(MethodCallHandlerImpl.EXTRA_ACTION) && channel != null) {
|
if (intent.hasExtra(QuickActions.EXTRA_ACTION) && activity != null) {
|
||||||
Context context = activity.getApplicationContext();
|
Context context = activity.getApplicationContext();
|
||||||
ShortcutManager shortcutManager =
|
ShortcutManager shortcutManager =
|
||||||
(ShortcutManager) context.getSystemService(Context.SHORTCUT_SERVICE);
|
(ShortcutManager) context.getSystemService(Context.SHORTCUT_SERVICE);
|
||||||
String shortcutId = intent.getStringExtra(MethodCallHandlerImpl.EXTRA_ACTION);
|
String shortcutId = intent.getStringExtra(QuickActions.EXTRA_ACTION);
|
||||||
channel.invokeMethod("launch", shortcutId);
|
quickActionsFlutterApi.launchAction(
|
||||||
|
shortcutId,
|
||||||
|
value -> {
|
||||||
|
// noop
|
||||||
|
});
|
||||||
shortcutManager.reportShortcutUsed(shortcutId);
|
shortcutManager.reportShortcutUsed(shortcutId);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupChannel(BinaryMessenger messenger, Context context, Activity activity) {
|
|
||||||
channel = new MethodChannel(messenger, CHANNEL_ID);
|
|
||||||
handler = new MethodCallHandlerImpl(context, activity);
|
|
||||||
channel.setMethodCallHandler(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void teardownChannel() {
|
|
||||||
channel.setMethodCallHandler(null);
|
|
||||||
channel = null;
|
|
||||||
handler = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,9 @@
|
|||||||
|
|
||||||
package io.flutter.plugins.quickactions;
|
package io.flutter.plugins.quickactions;
|
||||||
|
|
||||||
import static io.flutter.plugins.quickactions.MethodCallHandlerImpl.EXTRA_ACTION;
|
import static io.flutter.plugins.quickactions.QuickActions.EXTRA_ACTION;
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@ -21,15 +19,12 @@ import androidx.annotation.Nullable;
|
|||||||
import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding;
|
import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding;
|
||||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
|
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
|
||||||
import io.flutter.plugin.common.BinaryMessenger;
|
import io.flutter.plugin.common.BinaryMessenger;
|
||||||
import io.flutter.plugin.common.MethodCall;
|
|
||||||
import io.flutter.plugin.common.StandardMethodCodec;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class QuickActionsTest {
|
public class QuickActionsTest {
|
||||||
private static class TestBinaryMessenger implements BinaryMessenger {
|
private static class TestBinaryMessenger implements BinaryMessenger {
|
||||||
public MethodCall lastMethodCall;
|
public boolean launchActionCalled;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void send(@NonNull String channel, @Nullable ByteBuffer message) {
|
public void send(@NonNull String channel, @Nullable ByteBuffer message) {
|
||||||
@ -41,9 +36,8 @@ public class QuickActionsTest {
|
|||||||
@NonNull String channel,
|
@NonNull String channel,
|
||||||
@Nullable ByteBuffer message,
|
@Nullable ByteBuffer message,
|
||||||
@Nullable final BinaryReply callback) {
|
@Nullable final BinaryReply callback) {
|
||||||
if (channel.equals("plugins.flutter.io/quick_actions_android")) {
|
if (channel.contains("launchAction")) {
|
||||||
lastMethodCall =
|
launchActionCalled = true;
|
||||||
StandardMethodCodec.INSTANCE.decodeMethodCall((ByteBuffer) message.position(0));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,9 +69,6 @@ public class QuickActionsTest {
|
|||||||
final QuickActionsPlugin plugin =
|
final QuickActionsPlugin plugin =
|
||||||
new QuickActionsPlugin((version) -> SUPPORTED_BUILD >= version);
|
new QuickActionsPlugin((version) -> SUPPORTED_BUILD >= version);
|
||||||
setUpMessengerAndFlutterPluginBinding(testBinaryMessenger, plugin);
|
setUpMessengerAndFlutterPluginBinding(testBinaryMessenger, plugin);
|
||||||
Field handler = plugin.getClass().getDeclaredField("handler");
|
|
||||||
handler.setAccessible(true);
|
|
||||||
handler.set(plugin, mock(MethodCallHandlerImpl.class));
|
|
||||||
final Intent mockIntent = createMockIntentWithQuickActionExtra();
|
final Intent mockIntent = createMockIntentWithQuickActionExtra();
|
||||||
final Activity mockMainActivity = mock(Activity.class);
|
final Activity mockMainActivity = mock(Activity.class);
|
||||||
when(mockMainActivity.getIntent()).thenReturn(mockIntent);
|
when(mockMainActivity.getIntent()).thenReturn(mockIntent);
|
||||||
@ -93,9 +84,7 @@ public class QuickActionsTest {
|
|||||||
plugin.onAttachedToActivity(mockActivityPluginBinding);
|
plugin.onAttachedToActivity(mockActivityPluginBinding);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
assertNotNull(testBinaryMessenger.lastMethodCall);
|
assertTrue(testBinaryMessenger.launchActionCalled);
|
||||||
assertEquals(testBinaryMessenger.lastMethodCall.method, "launch");
|
|
||||||
assertEquals(testBinaryMessenger.lastMethodCall.arguments, SHORTCUT_TYPE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -111,7 +100,7 @@ public class QuickActionsTest {
|
|||||||
final boolean onNewIntentReturn = plugin.onNewIntent(mockIntent);
|
final boolean onNewIntentReturn = plugin.onNewIntent(mockIntent);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
assertNull(testBinaryMessenger.lastMethodCall);
|
assertFalse(testBinaryMessenger.launchActionCalled);
|
||||||
assertFalse(onNewIntentReturn);
|
assertFalse(onNewIntentReturn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,9 +126,7 @@ public class QuickActionsTest {
|
|||||||
final boolean onNewIntentReturn = plugin.onNewIntent(mockIntent);
|
final boolean onNewIntentReturn = plugin.onNewIntent(mockIntent);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
assertNotNull(testBinaryMessenger.lastMethodCall);
|
assertTrue(testBinaryMessenger.launchActionCalled);
|
||||||
assertEquals(testBinaryMessenger.lastMethodCall.method, "launch");
|
|
||||||
assertEquals(testBinaryMessenger.lastMethodCall.arguments, SHORTCUT_TYPE);
|
|
||||||
assertFalse(onNewIntentReturn);
|
assertFalse(onNewIntentReturn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,54 +3,62 @@
|
|||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:quick_actions_platform_interface/quick_actions_platform_interface.dart';
|
import 'package:quick_actions_platform_interface/quick_actions_platform_interface.dart';
|
||||||
|
|
||||||
|
import 'src/messages.g.dart';
|
||||||
|
|
||||||
export 'package:quick_actions_platform_interface/types/types.dart';
|
export 'package:quick_actions_platform_interface/types/types.dart';
|
||||||
|
|
||||||
const MethodChannel _channel =
|
late QuickActionHandler _handler;
|
||||||
MethodChannel('plugins.flutter.io/quick_actions_android');
|
|
||||||
|
|
||||||
/// An implementation of [QuickActionsPlatform] that for Android.
|
/// An implementation of [QuickActionsPlatform] for Android.
|
||||||
class QuickActionsAndroid extends QuickActionsPlatform {
|
class QuickActionsAndroid extends QuickActionsPlatform {
|
||||||
|
/// Creates a new plugin implementation instance.
|
||||||
|
QuickActionsAndroid({
|
||||||
|
@visibleForTesting AndroidQuickActionsApi? api,
|
||||||
|
}) : _hostApi = api ?? AndroidQuickActionsApi();
|
||||||
|
|
||||||
|
final AndroidQuickActionsApi _hostApi;
|
||||||
|
|
||||||
/// Registers this class as the default instance of [QuickActionsPlatform].
|
/// Registers this class as the default instance of [QuickActionsPlatform].
|
||||||
static void registerWith() {
|
static void registerWith() {
|
||||||
QuickActionsPlatform.instance = QuickActionsAndroid();
|
QuickActionsPlatform.instance = QuickActionsAndroid();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The MethodChannel that is being used by this implementation of the plugin.
|
|
||||||
@visibleForTesting
|
|
||||||
MethodChannel get channel => _channel;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> initialize(QuickActionHandler handler) async {
|
Future<void> initialize(QuickActionHandler handler) async {
|
||||||
channel.setMethodCallHandler((MethodCall call) async {
|
final _QuickActionHandlerApi quickActionsHandlerApi =
|
||||||
assert(call.method == 'launch');
|
_QuickActionHandlerApi();
|
||||||
handler(call.arguments as String);
|
AndroidQuickActionsFlutterApi.setup(quickActionsHandlerApi);
|
||||||
});
|
_handler = handler;
|
||||||
final String? action =
|
final String? action = await _hostApi.getLaunchAction();
|
||||||
await channel.invokeMethod<String?>('getLaunchAction');
|
|
||||||
if (action != null) {
|
if (action != null) {
|
||||||
handler(action);
|
_handler(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> setShortcutItems(List<ShortcutItem> items) async {
|
Future<void> setShortcutItems(List<ShortcutItem> items) async {
|
||||||
final List<Map<String, String?>> itemsList =
|
await _hostApi.setShortcutItems(
|
||||||
items.map(_serializeItem).toList();
|
items.map(_shortcutItemToShortcutItemMessage).toList(),
|
||||||
await channel.invokeMethod<void>('setShortcutItems', itemsList);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> clearShortcutItems() =>
|
Future<void> clearShortcutItems() => _hostApi.clearShortcutItems();
|
||||||
channel.invokeMethod<void>('clearShortcutItems');
|
|
||||||
|
|
||||||
Map<String, String?> _serializeItem(ShortcutItem item) {
|
ShortcutItemMessage _shortcutItemToShortcutItemMessage(ShortcutItem item) {
|
||||||
return <String, String?>{
|
return ShortcutItemMessage(
|
||||||
'type': item.type,
|
type: item.type,
|
||||||
'localizedTitle': item.localizedTitle,
|
localizedTitle: item.localizedTitle,
|
||||||
'icon': item.icon,
|
icon: item.icon,
|
||||||
};
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _QuickActionHandlerApi extends AndroidQuickActionsFlutterApi {
|
||||||
|
@override
|
||||||
|
void launchAction(String action) {
|
||||||
|
_handler(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,183 @@
|
|||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
// Autogenerated from Pigeon (v11.0.1), do not edit directly.
|
||||||
|
// See also: https://pub.dev/packages/pigeon
|
||||||
|
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
/// Home screen quick-action shortcut item.
|
||||||
|
class ShortcutItemMessage {
|
||||||
|
ShortcutItemMessage({
|
||||||
|
required this.type,
|
||||||
|
required this.localizedTitle,
|
||||||
|
this.icon,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// The identifier of this item; should be unique within the app.
|
||||||
|
String type;
|
||||||
|
|
||||||
|
/// Localized title of the item.
|
||||||
|
String localizedTitle;
|
||||||
|
|
||||||
|
/// Name of native resource to be displayed as the icon for this item.
|
||||||
|
String? icon;
|
||||||
|
|
||||||
|
Object encode() {
|
||||||
|
return <Object?>[
|
||||||
|
type,
|
||||||
|
localizedTitle,
|
||||||
|
icon,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
static ShortcutItemMessage decode(Object result) {
|
||||||
|
result as List<Object?>;
|
||||||
|
return ShortcutItemMessage(
|
||||||
|
type: result[0]! as String,
|
||||||
|
localizedTitle: result[1]! as String,
|
||||||
|
icon: result[2] as String?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AndroidQuickActionsApiCodec extends StandardMessageCodec {
|
||||||
|
const _AndroidQuickActionsApiCodec();
|
||||||
|
@override
|
||||||
|
void writeValue(WriteBuffer buffer, Object? value) {
|
||||||
|
if (value is ShortcutItemMessage) {
|
||||||
|
buffer.putUint8(128);
|
||||||
|
writeValue(buffer, value.encode());
|
||||||
|
} else {
|
||||||
|
super.writeValue(buffer, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object? readValueOfType(int type, ReadBuffer buffer) {
|
||||||
|
switch (type) {
|
||||||
|
case 128:
|
||||||
|
return ShortcutItemMessage.decode(readValue(buffer)!);
|
||||||
|
default:
|
||||||
|
return super.readValueOfType(type, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AndroidQuickActionsApi {
|
||||||
|
/// Constructor for [AndroidQuickActionsApi]. The [binaryMessenger] named argument is
|
||||||
|
/// available for dependency injection. If it is left null, the default
|
||||||
|
/// BinaryMessenger will be used which routes to the host platform.
|
||||||
|
AndroidQuickActionsApi({BinaryMessenger? binaryMessenger})
|
||||||
|
: _binaryMessenger = binaryMessenger;
|
||||||
|
final BinaryMessenger? _binaryMessenger;
|
||||||
|
|
||||||
|
static const MessageCodec<Object?> codec = _AndroidQuickActionsApiCodec();
|
||||||
|
|
||||||
|
/// Checks for, and returns the action that launched the app.
|
||||||
|
Future<String?> getLaunchAction() async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.quick_actions_android.AndroidQuickActionsApi.getLaunchAction',
|
||||||
|
codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList = await channel.send(null) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (replyList[0] as String?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the dynamic shortcuts for the app.
|
||||||
|
Future<void> setShortcutItems(
|
||||||
|
List<ShortcutItemMessage?> arg_itemsList) async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.quick_actions_android.AndroidQuickActionsApi.setShortcutItems',
|
||||||
|
codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(<Object?>[arg_itemsList]) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes all dynamic shortcuts.
|
||||||
|
Future<void> clearShortcutItems() async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.quick_actions_android.AndroidQuickActionsApi.clearShortcutItems',
|
||||||
|
codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList = await channel.send(null) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class AndroidQuickActionsFlutterApi {
|
||||||
|
static const MessageCodec<Object?> codec = StandardMessageCodec();
|
||||||
|
|
||||||
|
/// Sends a string representing a shortcut from the native platform to the app.
|
||||||
|
void launchAction(String action);
|
||||||
|
|
||||||
|
static void setup(AndroidQuickActionsFlutterApi? api,
|
||||||
|
{BinaryMessenger? binaryMessenger}) {
|
||||||
|
{
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.quick_actions_android.AndroidQuickActionsFlutterApi.launchAction',
|
||||||
|
codec,
|
||||||
|
binaryMessenger: binaryMessenger);
|
||||||
|
if (api == null) {
|
||||||
|
channel.setMessageHandler(null);
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler((Object? message) async {
|
||||||
|
assert(message != null,
|
||||||
|
'Argument for dev.flutter.pigeon.quick_actions_android.AndroidQuickActionsFlutterApi.launchAction was null.');
|
||||||
|
final List<Object?> args = (message as List<Object?>?)!;
|
||||||
|
final String? arg_action = (args[0] as String?);
|
||||||
|
assert(arg_action != null,
|
||||||
|
'Argument for dev.flutter.pigeon.quick_actions_android.AndroidQuickActionsFlutterApi.launchAction was null, expected non-null String.');
|
||||||
|
api.launchAction(arg_action!);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
Use of this source code is governed by a BSD-style license that can be
|
||||||
|
found in the LICENSE file.
|
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:pigeon/pigeon.dart';
|
||||||
|
|
||||||
|
@ConfigurePigeon(PigeonOptions(
|
||||||
|
dartOut: 'lib/src/messages.g.dart',
|
||||||
|
javaOut:
|
||||||
|
'android/src/main/java/io/flutter/plugins/quickactions/Messages.java',
|
||||||
|
javaOptions: JavaOptions(
|
||||||
|
package: 'io.flutter.plugins.quickactions',
|
||||||
|
),
|
||||||
|
copyrightHeader: 'pigeons/copyright.txt',
|
||||||
|
))
|
||||||
|
|
||||||
|
/// Home screen quick-action shortcut item.
|
||||||
|
class ShortcutItemMessage {
|
||||||
|
ShortcutItemMessage(
|
||||||
|
this.type,
|
||||||
|
this.localizedTitle,
|
||||||
|
this.icon,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// The identifier of this item; should be unique within the app.
|
||||||
|
String type;
|
||||||
|
|
||||||
|
/// Localized title of the item.
|
||||||
|
String localizedTitle;
|
||||||
|
|
||||||
|
/// Name of native resource to be displayed as the icon for this item.
|
||||||
|
String? icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostApi()
|
||||||
|
abstract class AndroidQuickActionsApi {
|
||||||
|
/// Checks for, and returns the action that launched the app.
|
||||||
|
String? getLaunchAction();
|
||||||
|
|
||||||
|
/// Sets the dynamic shortcuts for the app.
|
||||||
|
@async
|
||||||
|
void setShortcutItems(List<ShortcutItemMessage> itemsList);
|
||||||
|
|
||||||
|
/// Removes all dynamic shortcuts.
|
||||||
|
void clearShortcutItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FlutterApi()
|
||||||
|
abstract class AndroidQuickActionsFlutterApi {
|
||||||
|
/// Sends a string representing a shortcut from the native platform to the app.
|
||||||
|
void launchAction(String action);
|
||||||
|
}
|
@ -2,7 +2,7 @@ name: quick_actions_android
|
|||||||
description: An implementation for the Android platform of the Flutter `quick_actions` plugin.
|
description: An implementation for the Android platform of the Flutter `quick_actions` plugin.
|
||||||
repository: https://github.com/flutter/packages/tree/main/packages/quick_actions/quick_actions_android
|
repository: https://github.com/flutter/packages/tree/main/packages/quick_actions/quick_actions_android
|
||||||
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22
|
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22
|
||||||
version: 1.0.8
|
version: 1.0.9
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.19.0 <4.0.0"
|
sdk: ">=2.19.0 <4.0.0"
|
||||||
@ -27,6 +27,7 @@ dev_dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
integration_test:
|
integration_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
pigeon: ^11.0.1
|
||||||
plugin_platform_interface: ^2.1.2
|
plugin_platform_interface: ^2.1.2
|
||||||
|
|
||||||
topics:
|
topics:
|
||||||
|
@ -4,169 +4,104 @@
|
|||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:quick_actions_android/quick_actions_android.dart';
|
import 'package:quick_actions_android/quick_actions_android.dart';
|
||||||
|
import 'package:quick_actions_android/src/messages.g.dart';
|
||||||
import 'package:quick_actions_platform_interface/quick_actions_platform_interface.dart';
|
import 'package:quick_actions_platform_interface/quick_actions_platform_interface.dart';
|
||||||
|
|
||||||
|
const String LAUNCH_ACTION_STRING = 'aString';
|
||||||
|
|
||||||
|
/// Conversion tool to change [ShortcutItemMessage] back to [ShortcutItem]
|
||||||
|
ShortcutItem shortcutItemMessageToShortcutItem(ShortcutItemMessage item) {
|
||||||
|
return ShortcutItem(
|
||||||
|
type: item.type,
|
||||||
|
localizedTitle: item.localizedTitle,
|
||||||
|
icon: item.icon,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
TestWidgetsFlutterBinding.ensureInitialized();
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
group('$QuickActionsAndroid', () {
|
final _FakeQuickActionsApi api = _FakeQuickActionsApi();
|
||||||
late List<MethodCall> log;
|
final QuickActionsAndroid quickActions = QuickActionsAndroid(api: api);
|
||||||
|
|
||||||
setUp(() {
|
test('registerWith() registers correct instance', () {
|
||||||
log = <MethodCall>[];
|
QuickActionsAndroid.registerWith();
|
||||||
|
expect(QuickActionsPlatform.instance, isA<QuickActionsAndroid>());
|
||||||
|
});
|
||||||
|
|
||||||
|
group('#initialize', () {
|
||||||
|
test('passes getLaunchAction on launch method', () {
|
||||||
|
quickActions.initialize((String type) {});
|
||||||
|
|
||||||
|
expect(api.getLaunchActionCalled, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
QuickActionsAndroid buildQuickActionsPlugin() {
|
test('initialize', () async {
|
||||||
final QuickActionsAndroid quickActions = QuickActionsAndroid();
|
final Completer<bool> quickActionsHandler = Completer<bool>();
|
||||||
_ambiguate(TestDefaultBinaryMessengerBinding.instance)!
|
await quickActions.initialize((_) => quickActionsHandler.complete(true));
|
||||||
.defaultBinaryMessenger
|
|
||||||
.setMockMethodCallHandler(quickActions.channel,
|
|
||||||
(MethodCall methodCall) async {
|
|
||||||
log.add(methodCall);
|
|
||||||
return '';
|
|
||||||
});
|
|
||||||
|
|
||||||
return quickActions;
|
expect(quickActionsHandler.future, completion(isTrue));
|
||||||
}
|
|
||||||
|
|
||||||
test('registerWith() registers correct instance', () {
|
|
||||||
QuickActionsAndroid.registerWith();
|
|
||||||
expect(QuickActionsPlatform.instance, isA<QuickActionsAndroid>());
|
|
||||||
});
|
|
||||||
|
|
||||||
group('#initialize', () {
|
|
||||||
test('passes getLaunchAction on launch method', () {
|
|
||||||
final QuickActionsAndroid quickActions = buildQuickActionsPlugin();
|
|
||||||
quickActions.initialize((String type) {});
|
|
||||||
|
|
||||||
expect(
|
|
||||||
log,
|
|
||||||
<Matcher>[
|
|
||||||
isMethodCall('getLaunchAction', arguments: null),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('initialize', () async {
|
|
||||||
final QuickActionsAndroid quickActions = buildQuickActionsPlugin();
|
|
||||||
final Completer<bool> quickActionsHandler = Completer<bool>();
|
|
||||||
await quickActions
|
|
||||||
.initialize((_) => quickActionsHandler.complete(true));
|
|
||||||
expect(
|
|
||||||
log,
|
|
||||||
<Matcher>[
|
|
||||||
isMethodCall('getLaunchAction', arguments: null),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
log.clear();
|
|
||||||
|
|
||||||
expect(quickActionsHandler.future, completion(isTrue));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group('#setShortCutItems', () {
|
|
||||||
test('passes shortcutItem through channel', () {
|
|
||||||
final QuickActionsAndroid quickActions = buildQuickActionsPlugin();
|
|
||||||
quickActions.initialize((String type) {});
|
|
||||||
quickActions.setShortcutItems(<ShortcutItem>[
|
|
||||||
const ShortcutItem(
|
|
||||||
type: 'test', localizedTitle: 'title', icon: 'icon.svg')
|
|
||||||
]);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
log,
|
|
||||||
<Matcher>[
|
|
||||||
isMethodCall('getLaunchAction', arguments: null),
|
|
||||||
isMethodCall('setShortcutItems', arguments: <Map<String, String>>[
|
|
||||||
<String, String>{
|
|
||||||
'type': 'test',
|
|
||||||
'localizedTitle': 'title',
|
|
||||||
'icon': 'icon.svg',
|
|
||||||
}
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('setShortcutItems with demo data', () async {
|
|
||||||
const String type = 'type';
|
|
||||||
const String localizedTitle = 'localizedTitle';
|
|
||||||
const String icon = 'icon';
|
|
||||||
final QuickActionsAndroid quickActions = buildQuickActionsPlugin();
|
|
||||||
await quickActions.setShortcutItems(
|
|
||||||
const <ShortcutItem>[
|
|
||||||
ShortcutItem(type: type, localizedTitle: localizedTitle, icon: icon)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
log,
|
|
||||||
<Matcher>[
|
|
||||||
isMethodCall(
|
|
||||||
'setShortcutItems',
|
|
||||||
arguments: <Map<String, String>>[
|
|
||||||
<String, String>{
|
|
||||||
'type': type,
|
|
||||||
'localizedTitle': localizedTitle,
|
|
||||||
'icon': icon,
|
|
||||||
}
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
log.clear();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group('#clearShortCutItems', () {
|
|
||||||
test('send clearShortcutItems through channel', () {
|
|
||||||
final QuickActionsAndroid quickActions = buildQuickActionsPlugin();
|
|
||||||
quickActions.initialize((String type) {});
|
|
||||||
quickActions.clearShortcutItems();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
log,
|
|
||||||
<Matcher>[
|
|
||||||
isMethodCall('getLaunchAction', arguments: null),
|
|
||||||
isMethodCall('clearShortcutItems', arguments: null),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('clearShortcutItems', () {
|
|
||||||
final QuickActionsAndroid quickActions = buildQuickActionsPlugin();
|
|
||||||
quickActions.clearShortcutItems();
|
|
||||||
expect(
|
|
||||||
log,
|
|
||||||
<Matcher>[
|
|
||||||
isMethodCall('clearShortcutItems', arguments: null),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
log.clear();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('$ShortcutItem', () {
|
test('setShortCutItems', () async {
|
||||||
test('Shortcut item can be constructed', () {
|
await quickActions.initialize((String type) {});
|
||||||
const String type = 'type';
|
const ShortcutItem item =
|
||||||
const String localizedTitle = 'title';
|
ShortcutItem(type: 'test', localizedTitle: 'title', icon: 'icon.svg');
|
||||||
const String icon = 'foo';
|
await quickActions.setShortcutItems(<ShortcutItem>[item]);
|
||||||
|
|
||||||
const ShortcutItem item =
|
expect(api.items.first.type, item.type);
|
||||||
ShortcutItem(type: type, localizedTitle: localizedTitle, icon: icon);
|
expect(api.items.first.localizedTitle, item.localizedTitle);
|
||||||
|
expect(api.items.first.icon, item.icon);
|
||||||
|
});
|
||||||
|
|
||||||
expect(item.type, type);
|
test('clearShortCutItems', () {
|
||||||
expect(item.localizedTitle, localizedTitle);
|
quickActions.initialize((String type) {});
|
||||||
expect(item.icon, icon);
|
const ShortcutItem item =
|
||||||
});
|
ShortcutItem(type: 'test', localizedTitle: 'title', icon: 'icon.svg');
|
||||||
|
quickActions.setShortcutItems(<ShortcutItem>[item]);
|
||||||
|
quickActions.clearShortcutItems();
|
||||||
|
|
||||||
|
expect(api.items.isEmpty, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Shortcut item can be constructed', () {
|
||||||
|
const String type = 'type';
|
||||||
|
const String localizedTitle = 'title';
|
||||||
|
const String icon = 'foo';
|
||||||
|
|
||||||
|
const ShortcutItem item =
|
||||||
|
ShortcutItem(type: type, localizedTitle: localizedTitle, icon: icon);
|
||||||
|
|
||||||
|
expect(item.type, type);
|
||||||
|
expect(item.localizedTitle, localizedTitle);
|
||||||
|
expect(item.icon, icon);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This allows a value of type T or T? to be treated as a value of type T?.
|
class _FakeQuickActionsApi implements AndroidQuickActionsApi {
|
||||||
///
|
List<ShortcutItem> items = <ShortcutItem>[];
|
||||||
/// We use this so that APIs that have become non-nullable can still be used
|
bool getLaunchActionCalled = false;
|
||||||
/// with `!` and `?` on the stable branch.
|
|
||||||
T? _ambiguate<T>(T? value) => value;
|
@override
|
||||||
|
Future<void> clearShortcutItems() async {
|
||||||
|
items = <ShortcutItem>[];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String?> getLaunchAction() async {
|
||||||
|
getLaunchActionCalled = true;
|
||||||
|
return LAUNCH_ACTION_STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> setShortcutItems(List<ShortcutItemMessage?> itemsList) async {
|
||||||
|
await clearShortcutItems();
|
||||||
|
for (final ShortcutItemMessage? element in itemsList) {
|
||||||
|
items.add(shortcutItemMessageToShortcutItem(element!));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user