diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml
index 63577c2..0ef6ec2 100644
--- a/example/android/app/src/main/AndroidManifest.xml
+++ b/example/android/app/src/main/AndroidManifest.xml
@@ -23,8 +23,8 @@
-
-
+
+
{
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
+
+ ElevatedButton(
+ onPressed: () {
+ MoxplatformPlugin.contacts.recordSentMessage('Hallo', 'Welt');
+ },
+ child: Text('Test recordSentMessage (no fallback)'),
+ ),
+ ElevatedButton(
+ onPressed: () {
+ MoxplatformPlugin.contacts.recordSentMessage('Person', 'Person', fallbackIcon: FallbackIconType.person);
+ },
+ child: Text('Test recordSentMessage (person fallback)'),
+ ),
+ ElevatedButton(
+ onPressed: () {
+ MoxplatformPlugin.contacts.recordSentMessage('Notes', 'Notes', fallbackIcon: FallbackIconType.notes);
+ },
+ child: Text('Test recordSentMessage (notes fallback)'),
+ ),
],
),
),
diff --git a/flake.lock b/flake.lock
index a126c34..390e1fe 100644
--- a/flake.lock
+++ b/flake.lock
@@ -1,6 +1,66 @@
{
"nodes": {
+ "android-nixpkgs": {
+ "inputs": {
+ "devshell": "devshell",
+ "flake-utils": "flake-utils",
+ "nixpkgs": "nixpkgs"
+ },
+ "locked": {
+ "lastModified": 1689798050,
+ "narHash": "sha256-ZyFPra7N0MF803o55dYQQyX9b/BmXr6QTCyN7slRThY=",
+ "owner": "tadfisher",
+ "repo": "android-nixpkgs",
+ "rev": "9aa0e2990da86de8ca203af313668851dcb9ea6e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "tadfisher",
+ "repo": "android-nixpkgs",
+ "type": "github"
+ }
+ },
+ "devshell": {
+ "inputs": {
+ "nixpkgs": [
+ "android-nixpkgs",
+ "nixpkgs"
+ ],
+ "systems": "systems"
+ },
+ "locked": {
+ "lastModified": 1688380630,
+ "narHash": "sha256-8ilApWVb1mAi4439zS3iFeIT0ODlbrifm/fegWwgHjA=",
+ "owner": "numtide",
+ "repo": "devshell",
+ "rev": "f9238ec3d75cefbb2b42a44948c4e8fb1ae9a205",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "devshell",
+ "type": "github"
+ }
+ },
"flake-utils": {
+ "inputs": {
+ "systems": "systems_2"
+ },
+ "locked": {
+ "lastModified": 1689068808,
+ "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "flake-utils_2": {
"locked": {
"lastModified": 1649676176,
"narHash": "sha256-OWKJratjt2RW151VUlJPRALb7OU2S5s+f0vLj4o1bHM=",
@@ -17,11 +77,27 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1660551188,
- "narHash": "sha256-a1LARMMYQ8DPx1BgoI/UN4bXe12hhZkCNqdxNi6uS0g=",
+ "lastModified": 1689679375,
+ "narHash": "sha256-LHUC52WvyVDi9PwyL1QCpaxYWBqp4ir4iL6zgOkmcb8=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "441dc5d512153039f19ef198e662e4f3dbb9fd65",
+ "rev": "684c17c429c42515bafb3ad775d2a710947f3d67",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs_2": {
+ "locked": {
+ "lastModified": 1689752456,
+ "narHash": "sha256-VOChdECcEI8ixz8QY+YC4JaNEFwQd1V8bA0G4B28Ki0=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "7f256d7da238cb627ef189d56ed590739f42f13b",
"type": "github"
},
"original": {
@@ -33,8 +109,39 @@
},
"root": {
"inputs": {
- "flake-utils": "flake-utils",
- "nixpkgs": "nixpkgs"
+ "android-nixpkgs": "android-nixpkgs",
+ "flake-utils": "flake-utils_2",
+ "nixpkgs": "nixpkgs_2"
+ }
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1681028828,
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "owner": "nix-systems",
+ "repo": "default",
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default",
+ "type": "github"
+ }
+ },
+ "systems_2": {
+ "locked": {
+ "lastModified": 1681028828,
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "owner": "nix-systems",
+ "repo": "default",
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default",
+ "type": "github"
}
}
},
diff --git a/flake.nix b/flake.nix
index f3277ab..f50d662 100644
--- a/flake.nix
+++ b/flake.nix
@@ -3,9 +3,10 @@
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
+ android-nixpkgs.url = "github:tadfisher/android-nixpkgs";
};
- outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system: let
+ outputs = { self, nixpkgs, flake-utils, android-nixpkgs }: flake-utils.lib.eachDefaultSystem (system: let
pkgs = import nixpkgs {
inherit system;
config = {
@@ -13,29 +14,32 @@
allowUnfree = true;
};
};
- android = pkgs.androidenv.composeAndroidPackages {
- # TODO: Find a way to pin these
- #toolsVersion = "26.1.1";
- #platformToolsVersion = "31.0.3";
- #buildToolsVersions = [ "31.0.0" ];
- #includeEmulator = true;
- #emulatorVersion = "30.6.3";
- platformVersions = [ "28" ];
- includeSources = false;
- includeSystemImages = true;
- systemImageTypes = [ "default" ];
- abiVersions = [ "x86_64" ];
- includeNDK = false;
- useGoogleAPIs = false;
- useGoogleTVAddOns = false;
- };
- pinnedJDK = pkgs.jdk;
+ # Everything to make Flutter happy
+ sdk = android-nixpkgs.sdk.${system} (sdkPkgs: with sdkPkgs; [
+ cmdline-tools-latest
+ build-tools-30-0-3
+ build-tools-33-0-2
+ build-tools-34-0-0
+ platform-tools
+ emulator
+ patcher-v4
+ platforms-android-30
+ platforms-android-31
+ platforms-android-33
+ ]);
+ pinnedJDK = pkgs.jdk17;
in {
devShell = pkgs.mkShell {
buildInputs = with pkgs; [
- flutter pinnedJDK android.platform-tools dart # Flutter
- gitlint jq # Code hygiene
- ripgrep # General utilities
+ # Android
+ pinnedJDK
+ sdk
+
+ # Flutter
+ flutter dart
+
+ # Code hygiene
+ gitlint
# Flutter dependencies for linux desktop
atk
@@ -59,9 +63,13 @@
CPATH = "${pkgs.xorg.libX11.dev}/include:${pkgs.xorg.xorgproto}/include";
LD_LIBRARY_PATH = with pkgs; lib.makeLibraryPath [ atk cairo epoxy gdk-pixbuf glib gtk3 harfbuzz pango ];
- ANDROID_HOME = "${android.androidsdk}/libexec/android-sdk";
+ ANDROID_HOME = "${sdk}/share/android-sdk";
+ ANDROID_SDK_ROOT = "${sdk}/share/android-sdk";
JAVA_HOME = pinnedJDK;
- ANDROID_AVD_HOME = (toString ./.) + "/.android/avd";
+
+ # Fix an issue with Flutter using an older version of aapt2, which does not know
+ # an used parameter.
+ GRADLE_OPTS = "-Dorg.gradle.project.android.aapt2FromMavenOverride=${sdk}/share/android-sdk/build-tools/34.0.0/aapt2";
};
});
}
diff --git a/packages/moxplatform/lib/src/plugin.dart b/packages/moxplatform/lib/src/plugin.dart
index eadc4c9..d081d70 100644
--- a/packages/moxplatform/lib/src/plugin.dart
+++ b/packages/moxplatform/lib/src/plugin.dart
@@ -4,4 +4,5 @@ class MoxplatformPlugin {
static IsolateHandler get handler => MoxplatformInterface.handler;
static MediaScannerImplementation get media => MoxplatformInterface.media;
static CryptographyImplementation get crypto => MoxplatformInterface.crypto;
+ static ContactsImplementation get contacts => MoxplatformInterface.contacts;
}
diff --git a/packages/moxplatform_android/android/build.gradle b/packages/moxplatform_android/android/build.gradle
index b476504..5bf9414 100644
--- a/packages/moxplatform_android/android/build.gradle
+++ b/packages/moxplatform_android/android/build.gradle
@@ -25,8 +25,8 @@ android {
compileSdkVersion 31
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility JavaVersion.VERSION_14
+ targetCompatibility JavaVersion.VERSION_14
}
defaultConfig {
@@ -36,4 +36,5 @@ android {
dependencies {
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
+ implementation 'androidx.core:core:1.10.1'
}
\ No newline at end of file
diff --git a/packages/moxplatform_android/android/gradle/wrapper/gradle-wrapper.properties b/packages/moxplatform_android/android/gradle/wrapper/gradle-wrapper.properties
index da9702f..ffed3a2 100644
--- a/packages/moxplatform_android/android/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/moxplatform_android/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/packages/moxplatform_android/android/src/main/java/me/polynom/moxplatform_android/MoxplatformAndroidPlugin.java b/packages/moxplatform_android/android/src/main/java/me/polynom/moxplatform_android/MoxplatformAndroidPlugin.java
index cd79e95..5e8e1e3 100644
--- a/packages/moxplatform_android/android/src/main/java/me/polynom/moxplatform_android/MoxplatformAndroidPlugin.java
+++ b/packages/moxplatform_android/android/src/main/java/me/polynom/moxplatform_android/MoxplatformAndroidPlugin.java
@@ -6,17 +6,28 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Icon;
+import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.app.Person;
import androidx.core.content.ContextCompat;
+import androidx.core.content.pm.ShortcutInfoCompat;
+import androidx.core.content.pm.ShortcutManagerCompat;
+import androidx.core.graphics.drawable.IconCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import java.io.FileInputStream;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
@@ -191,12 +202,79 @@ public class MoxplatformAndroidPlugin extends BroadcastReceiver implements Flutt
});
hashingThread.start();
break;
+ case "recordSentMessage":
+ ArrayList rargs = (ArrayList) call.arguments;
+ try {
+ recordSentMessage(
+ (String) rargs.get(0),
+ (String) rargs.get(1),
+ (String) rargs.get(2),
+ (int) rargs.get(3)
+ );
+ } catch (ClassNotFoundException ex) {
+ Log.e(TAG, "Failed to get classname");
+ Log.e(TAG, ex.getMessage());
+ }
+ result.success(true);
+ break;
default:
result.notImplemented();
break;
}
}
+ private void recordSentMessage(String name, String jid, String avatarPath, int fallbackIconType) throws ClassNotFoundException {
+ // Very much inspired (or copied) from https://github.com/ShoutSocial/share_handler
+ final String pkgName = context.getPackageName();
+ final Intent intent = new Intent(context, Class.forName(pkgName + ".MainActivity"));
+ intent.setAction(Intent.ACTION_SEND);
+
+ // Compatibility with share_handler
+ intent.putExtra("conversationIdentifier", jid);
+
+ final String shortcutTarget = pkgName + ".dynamic_share_target";
+ final ShortcutInfoCompat.Builder builder = new ShortcutInfoCompat.Builder(context, name);
+ builder
+ .setShortLabel(name)
+ .setIsConversation()
+ .setCategories(Set.of(shortcutTarget))
+ .setIntent(intent)
+ .setLongLived(true);
+
+ // TODO: This is dumb. Maybe just raise the minimum Android version
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
+ final Person.Builder personBuilder = new Person.Builder()
+ .setKey(jid)
+ .setName(name);
+
+ if (avatarPath != null) {
+ final Bitmap bitmap = BitmapFactory.decodeFile(avatarPath);
+ final IconCompat icon = IconCompat.createWithAdaptiveBitmap(bitmap);
+ builder.setIcon(icon);
+ personBuilder.setIcon(icon);
+ } else {
+ if (fallbackIconType == 0 || fallbackIconType == 1) {
+ final int id = switch (fallbackIconType) {
+ default:
+ case 0: yield R.mipmap.person;
+ case 1: yield R.mipmap.notes;
+ };
+ final IconCompat personIcon = IconCompat.createWithResource(
+ context,
+ id
+ );
+ builder.setIcon(personIcon);
+ personBuilder.setIcon(personIcon);
+ }
+ }
+
+ builder.setPerson(personBuilder.build());
+ }
+
+ final ShortcutInfoCompat shortcut = builder.build();
+ ShortcutManagerCompat.addDynamicShortcuts(context, List.of(shortcut));
+ }
+
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() == null) return;
diff --git a/packages/moxplatform_android/android/src/main/res/drawable/notes_foreground.xml b/packages/moxplatform_android/android/src/main/res/drawable/notes_foreground.xml
new file mode 100644
index 0000000..54ce67b
--- /dev/null
+++ b/packages/moxplatform_android/android/src/main/res/drawable/notes_foreground.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
diff --git a/packages/moxplatform_android/android/src/main/res/drawable/person_foreground.xml b/packages/moxplatform_android/android/src/main/res/drawable/person_foreground.xml
new file mode 100644
index 0000000..af2529b
--- /dev/null
+++ b/packages/moxplatform_android/android/src/main/res/drawable/person_foreground.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
diff --git a/packages/moxplatform_android/android/src/main/res/mipmap-anydpi-v26/notes.xml b/packages/moxplatform_android/android/src/main/res/mipmap-anydpi-v26/notes.xml
new file mode 100644
index 0000000..7733c4b
--- /dev/null
+++ b/packages/moxplatform_android/android/src/main/res/mipmap-anydpi-v26/notes.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/moxplatform_android/android/src/main/res/mipmap-anydpi-v26/person.xml b/packages/moxplatform_android/android/src/main/res/mipmap-anydpi-v26/person.xml
new file mode 100644
index 0000000..0e663b7
--- /dev/null
+++ b/packages/moxplatform_android/android/src/main/res/mipmap-anydpi-v26/person.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/moxplatform_android/android/src/main/res/mipmap-xhdpi/notes.png b/packages/moxplatform_android/android/src/main/res/mipmap-xhdpi/notes.png
new file mode 100644
index 0000000..070bb9f
Binary files /dev/null and b/packages/moxplatform_android/android/src/main/res/mipmap-xhdpi/notes.png differ
diff --git a/packages/moxplatform_android/android/src/main/res/mipmap-xhdpi/person.png b/packages/moxplatform_android/android/src/main/res/mipmap-xhdpi/person.png
new file mode 100644
index 0000000..cff4b38
Binary files /dev/null and b/packages/moxplatform_android/android/src/main/res/mipmap-xhdpi/person.png differ
diff --git a/packages/moxplatform_android/android/src/main/res/values/notes_background.xml b/packages/moxplatform_android/android/src/main/res/values/notes_background.xml
new file mode 100644
index 0000000..40c90a7
--- /dev/null
+++ b/packages/moxplatform_android/android/src/main/res/values/notes_background.xml
@@ -0,0 +1,4 @@
+
+
+ #CF4AFF
+
\ No newline at end of file
diff --git a/packages/moxplatform_android/android/src/main/res/values/person_background.xml b/packages/moxplatform_android/android/src/main/res/values/person_background.xml
new file mode 100644
index 0000000..da3e5eb
--- /dev/null
+++ b/packages/moxplatform_android/android/src/main/res/values/person_background.xml
@@ -0,0 +1,4 @@
+
+
+ #CF4AFF
+
\ No newline at end of file
diff --git a/packages/moxplatform_android/lib/src/contacts_android.dart b/packages/moxplatform_android/lib/src/contacts_android.dart
new file mode 100644
index 0000000..6671897
--- /dev/null
+++ b/packages/moxplatform_android/lib/src/contacts_android.dart
@@ -0,0 +1,32 @@
+import 'package:flutter/services.dart';
+import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
+
+class AndroidContactsImplementation extends ContactsImplementation {
+ final _methodChannel = const MethodChannel('me.polynom.moxplatform_android');
+
+ @override
+ Future recordSentMessage(
+ String name,
+ String jid, {
+ String? avatarPath,
+ FallbackIconType fallbackIcon = FallbackIconType.none,
+ }) async {
+ // Ensure we always have an icon
+ if (avatarPath != null) {
+ assert(
+ fallbackIcon != FallbackIconType.none,
+ 'If no avatar is specified, then a fallbackIcon must be set',
+ );
+ }
+
+ await _methodChannel.invokeMethod(
+ 'recordSentMessage',
+ [
+ name,
+ jid,
+ avatarPath,
+ fallbackIcon.id,
+ ],
+ );
+ }
+}
diff --git a/packages/moxplatform_android/lib/src/plugin_android.dart b/packages/moxplatform_android/lib/src/plugin_android.dart
index 525bd80..a024c23 100644
--- a/packages/moxplatform_android/lib/src/plugin_android.dart
+++ b/packages/moxplatform_android/lib/src/plugin_android.dart
@@ -1,3 +1,4 @@
+import 'package:moxplatform_android/src/contacts_android.dart';
import 'package:moxplatform_android/src/crypto_android.dart';
import 'package:moxplatform_android/src/isolate_android.dart';
import 'package:moxplatform_android/src/media_android.dart';
@@ -7,9 +8,10 @@ class MoxplatformAndroidPlugin extends MoxplatformInterface {
static void registerWith() {
// ignore: avoid_print
print('MoxplatformAndroidPlugin: Registering implementation');
+ MoxplatformInterface.contacts = AndroidContactsImplementation();
+ MoxplatformInterface.crypto = AndroidCryptographyImplementation();
MoxplatformInterface.handler = AndroidIsolateHandler();
MoxplatformInterface.media = AndroidMediaScannerImplementation();
- MoxplatformInterface.crypto = AndroidCryptographyImplementation();
}
@override
diff --git a/packages/moxplatform_platform_interface/lib/moxplatform_platform_interface.dart b/packages/moxplatform_platform_interface/lib/moxplatform_platform_interface.dart
index 88c3336..9b2a14a 100644
--- a/packages/moxplatform_platform_interface/lib/moxplatform_platform_interface.dart
+++ b/packages/moxplatform_platform_interface/lib/moxplatform_platform_interface.dart
@@ -1,5 +1,7 @@
library moxplatform_platform_interface;
+export 'src/contacts.dart';
+export 'src/contacts_stub.dart';
export 'src/crypto.dart';
export 'src/crypto_stub.dart';
export 'src/interface.dart';
diff --git a/packages/moxplatform_platform_interface/lib/src/contacts.dart b/packages/moxplatform_platform_interface/lib/src/contacts.dart
new file mode 100644
index 0000000..3ee6a22
--- /dev/null
+++ b/packages/moxplatform_platform_interface/lib/src/contacts.dart
@@ -0,0 +1,22 @@
+// The type of icon to use when no avatar path is provided.
+enum FallbackIconType {
+ none(-1),
+ person(0),
+ notes(1);
+
+ const FallbackIconType(this.id);
+
+ // The ID of the fallback icon.
+ final int id;
+}
+
+// Wrapper around various contact APIs.
+// ignore: one_member_abstracts
+abstract class ContactsImplementation {
+ Future recordSentMessage(
+ String name,
+ String jid, {
+ String? avatarPath,
+ FallbackIconType fallbackIcon = FallbackIconType.none,
+ });
+}
diff --git a/packages/moxplatform_platform_interface/lib/src/contacts_stub.dart b/packages/moxplatform_platform_interface/lib/src/contacts_stub.dart
new file mode 100644
index 0000000..51a6dae
--- /dev/null
+++ b/packages/moxplatform_platform_interface/lib/src/contacts_stub.dart
@@ -0,0 +1,11 @@
+import 'package:moxplatform_platform_interface/src/contacts.dart';
+
+class StubContactsImplementation extends ContactsImplementation {
+ @override
+ Future recordSentMessage(
+ String name,
+ String jid, {
+ String? avatarPath,
+ FallbackIconType fallbackIcon = FallbackIconType.none,
+ }) async {}
+}
diff --git a/packages/moxplatform_platform_interface/lib/src/interface.dart b/packages/moxplatform_platform_interface/lib/src/interface.dart
index ca4ac6c..6005fb7 100644
--- a/packages/moxplatform_platform_interface/lib/src/interface.dart
+++ b/packages/moxplatform_platform_interface/lib/src/interface.dart
@@ -1,3 +1,5 @@
+import 'package:moxplatform_platform_interface/src/contacts.dart';
+import 'package:moxplatform_platform_interface/src/contacts_stub.dart';
import 'package:moxplatform_platform_interface/src/crypto.dart';
import 'package:moxplatform_platform_interface/src/crypto_stub.dart';
import 'package:moxplatform_platform_interface/src/isolate.dart';
@@ -14,6 +16,7 @@ abstract class MoxplatformInterface extends PlatformInterface {
static IsolateHandler handler = StubIsolateHandler();
static MediaScannerImplementation media = StubMediaScannerImplementation();
static CryptographyImplementation crypto = StubCryptographyImplementation();
+ static ContactsImplementation contacts = StubContactsImplementation();
/// Return the current platform name.
Future getPlatformName();
diff --git a/packages/moxplatform_platform_interface/packages/moxplatform_android/lib/src/contacts_android.dart b/packages/moxplatform_platform_interface/packages/moxplatform_android/lib/src/contacts_android.dart
new file mode 100644
index 0000000..e69de29
diff --git a/packages/moxplatform_platform_interface/packages/moxplatform_platform_interface/lib/src/contacts.dart b/packages/moxplatform_platform_interface/packages/moxplatform_platform_interface/lib/src/contacts.dart
new file mode 100644
index 0000000..e69de29
diff --git a/packages/moxplatform_platform_interface/packages/moxplatform_platform_interface/lib/src/contacts_stub.dart b/packages/moxplatform_platform_interface/packages/moxplatform_platform_interface/lib/src/contacts_stub.dart
new file mode 100644
index 0000000..e69de29