blob: 753541bf9338b4ac8fa3324a03a8c3f702ab1b87 [file] [log] [blame]
package io.flutter.plugins.androidintent;
import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/** Forwards incoming {@link MethodCall}s to {@link IntentSender#send}. */
public final class MethodCallHandlerImpl implements MethodCallHandler {
private static final String TAG = "MethodCallHandlerImpl";
private final IntentSender sender;
@Nullable private MethodChannel methodChannel;
/**
* Uses the given {@code sender} for all incoming calls.
*
* <p>This assumes that the sender's context and activity state are managed elsewhere and
* correctly initialized before being sent here.
*/
MethodCallHandlerImpl(IntentSender sender) {
this.sender = sender;
}
/**
* Registers this instance as a method call handler on the given {@code messenger}.
*
* <p>Stops any previously started and unstopped calls.
*
* <p>This should be cleaned with {@link #stopListening} once the messenger is disposed of.
*/
void startListening(BinaryMessenger messenger) {
if (methodChannel != null) {
Log.wtf(TAG, "Setting a method call handler before the last was disposed.");
stopListening();
}
methodChannel = new MethodChannel(messenger, "plugins.flutter.io/android_intent");
methodChannel.setMethodCallHandler(this);
}
/**
* Clears this instance from listening to method calls.
*
* <p>Does nothing is {@link #startListening} hasn't been called, or if we're already stopped.
*/
void stopListening() {
if (methodChannel == null) {
Log.d(TAG, "Tried to stop listening when no methodChannel had been initialized.");
return;
}
methodChannel.setMethodCallHandler(null);
methodChannel = null;
}
/**
* Parses the incoming call and forwards it to the cached {@link IntentSender}.
*
* <p>Always calls {@code result#success}.
*/
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
String action = convertAction((String) call.argument("action"));
Integer flags = call.argument("flags");
String category = call.argument("category");
String stringData = call.argument("data");
Uri data = call.argument("data") != null ? Uri.parse(stringData) : null;
Map<String, ?> stringMap = call.argument("arguments");
Bundle arguments = convertArguments(stringMap);
String packageName = call.argument("package");
String component = call.argument("componentName");
ComponentName componentName = null;
if (packageName != null
&& component != null
&& !TextUtils.isEmpty(packageName)
&& !TextUtils.isEmpty(component)) {
componentName = new ComponentName(packageName, component);
}
String type = call.argument("type");
Intent intent =
sender.buildIntent(
action, flags, category, data, arguments, packageName, componentName, type);
if ("launch".equalsIgnoreCase(call.method)) {
sender.send(intent);
result.success(null);
} else if ("canResolveActivity".equalsIgnoreCase(call.method)) {
result.success(sender.canResolveActivity(intent));
} else {
result.notImplemented();
}
}
private static String convertAction(String action) {
if (action == null) {
return null;
}
switch (action) {
case "action_view":
return Intent.ACTION_VIEW;
case "action_voice":
return Intent.ACTION_VOICE_COMMAND;
case "settings":
return Settings.ACTION_SETTINGS;
case "action_location_source_settings":
return Settings.ACTION_LOCATION_SOURCE_SETTINGS;
case "action_application_details_settings":
return Settings.ACTION_APPLICATION_DETAILS_SETTINGS;
default:
return action;
}
}
private static Bundle convertArguments(Map<String, ?> arguments) {
Bundle bundle = new Bundle();
if (arguments == null) {
return bundle;
}
for (String key : arguments.keySet()) {
Object value = arguments.get(key);
ArrayList<String> stringArrayList = isStringArrayList(value);
ArrayList<Integer> integerArrayList = isIntegerArrayList(value);
Map<String, ?> stringMap = isStringKeyedMap(value);
if (value instanceof Integer) {
bundle.putInt(key, (Integer) value);
} else if (value instanceof String) {
bundle.putString(key, (String) value);
} else if (value instanceof Boolean) {
bundle.putBoolean(key, (Boolean) value);
} else if (value instanceof Double) {
bundle.putDouble(key, (Double) value);
} else if (value instanceof Long) {
bundle.putLong(key, (Long) value);
} else if (value instanceof byte[]) {
bundle.putByteArray(key, (byte[]) value);
} else if (value instanceof int[]) {
bundle.putIntArray(key, (int[]) value);
} else if (value instanceof long[]) {
bundle.putLongArray(key, (long[]) value);
} else if (value instanceof double[]) {
bundle.putDoubleArray(key, (double[]) value);
} else if (integerArrayList != null) {
bundle.putIntegerArrayList(key, integerArrayList);
} else if (stringArrayList != null) {
bundle.putStringArrayList(key, stringArrayList);
} else if (stringMap != null) {
bundle.putBundle(key, convertArguments(stringMap));
} else {
throw new UnsupportedOperationException("Unsupported type " + value);
}
}
return bundle;
}
private static ArrayList<Integer> isIntegerArrayList(Object value) {
ArrayList<Integer> integerArrayList = new ArrayList<>();
if (!(value instanceof ArrayList)) {
return null;
}
ArrayList<?> intList = (ArrayList<?>) value;
for (Object o : intList) {
if (!(o instanceof Integer)) {
return null;
} else {
integerArrayList.add((Integer) o);
}
}
return integerArrayList;
}
private static ArrayList<String> isStringArrayList(Object value) {
ArrayList<String> stringArrayList = new ArrayList<>();
if (!(value instanceof ArrayList)) {
return null;
}
ArrayList<?> stringList = (ArrayList<?>) value;
for (Object o : stringList) {
if (!(o instanceof String)) {
return null;
} else {
stringArrayList.add((String) o);
}
}
return stringArrayList;
}
private static Map<String, ?> isStringKeyedMap(Object value) {
Map<String, Object> stringMap = new HashMap<>();
if (!(value instanceof Map)) {
return null;
}
Map<?, ?> mapValue = (Map<?, ?>) value;
for (Object key : mapValue.keySet()) {
if (!(key instanceof String)) {
return null;
} else {
Object o = mapValue.get(key);
if (o != null) {
stringMap.put((String) key, o);
}
}
}
return stringMap;
}
}