简体   繁体   中英

How to run a Xamarin.Forms Application on an Android Wear Device

How can I run a Xamarin.Forms.Platform.Android.FormsApplicationActivity on an Android Wear device? The call base.OnCreate(bundle) inside the onCreate method of my class always throws a RuntimeException "You cannot use indeterminate progress on a watch".

Here is my code:

namespace Test
{
    [Activity (Label = "Temp.Droid", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
    {
        protected override void OnCreate (Bundle bundle)
        {
            base.OnCreate (bundle);

            global::Xamarin.Forms.Forms.Init (this, bundle);

            LoadApplication (new App ());
        }
    }
}

The implementation of App should not matter since the exception gets already thrown on the call of the super onCreate and not by calling LoadApplication (new App ()) for loading the application. However its the base implementation generated by the project wizard for a Xamarin Mobile Application.

You would not run a Xamarin.Forms application on a wearable device. You would need to create a new Android Wear application in native Xamarin.Android. Wearable Applications use a special theme, special controls, and have special APIs. A good sample to look at is how I did Hanselman.Forms, which is a Xamarin.Forms main application but ties in an Android Wear application as well: https://github.com/jamesmontemagno/Hanselman.Forms

Despite the answer of James Montemagno I discovered it is possible to sync data in Xamarin Forms. I used the method of Vincent Maverick and incorporated it in Xamarin Forms. First of all take care you have the right Android SDK installed ( Android Wear Tutorial - A Comprehensive Introduction ). Assuming you hve your standard app, it is advised to create Wear app in a separate Xamarin Forms Cross Platform application. This because the Wear sizes are different from Phone sizes.

In both you Wear app and your phone app right click on the References of your Android project and select MANAGE NUGET PACKAGES. Browse for wear and select
Xamarin.GooglePlayServices.Wearable Version 29.0.0 (higher versions give problems).

在此处输入图片说明

Click on the Properties of your Android project in both applications. Make sure the Default Namespace (Application tab) and Package name (Android Manifest tab) are the same. Also make sure the Package name does not have capitals, this will cause problems in releasing your app to the Android store. Change the value of "Compile using Android version" to "API Level 21 (Xamarin.Android v5.0 Support).

在此处输入图片说明

In your Android MainActivity of both projects add usings

using Android.Gms.Wearable;
using Android.Gms.Common.Apis;
using Android.Support.V4.Content;

Then change your MainActivity of both applications to the following:

public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity, IDataApiDataListener, IMessageApiMessageListener
{
    private static GoogleApiClient client;
    const string _syncPath = "/MySyncPath/Data";
    static string device = "Watch";
    static string text= "";

    protected override void OnCreate(Bundle bundle)
    {
        TabLayoutResource = Resource.Layout.Tabbar;
        ToolbarResource = Resource.Layout.Toolbar;

        base.OnCreate(bundle);

        global::Xamarin.Forms.Forms.Init(this, bundle);
        LoadApplication(new App());

        client = new GoogleApiClient.Builder(this)
              .AddApi(WearableClass.API)
              .Build();
        IntentFilter filter = new IntentFilter(Intent.ActionSend);
        MessageReciever receiver = new MessageReciever(this);
        LocalBroadcastManager.GetInstance(this).RegisterReceiver(receiver, filter);
    }

    internal class MessageReciever : BroadcastReceiver
    {
        MainActivity _main;
        public MessageReciever(MainActivity owner) { this._main = owner; }
        public override void OnReceive(Context context, Intent intent)
        {
            _main.ProcessMessage(intent);
        }

    }

    public void OnDataChanged(DataEventBuffer dataEvents)
    {
        var dataEvent = Enumerable.Range(0, dataEvents.Count)
                                  .Select(i => dataEvents.Get(i).JavaCast<IDataEvent>())
                                  .FirstOrDefault(x => x.Type == DataEvent.TypeChanged && x.DataItem.Uri.Path.Equals(_syncPath));
        if (dataEvent == null)
            return;
        //do stuffs here
    }

    public override void OnBackPressed()
    {
           base.OnBackPressed();
    }

    protected override void OnStart()
    {
        base.OnStart();
        Android.Util.Log.Info("WearIntegration", "Received Message");

        client.Connect();
    }

    public void OnConnected(Bundle p0)
    {
        WearableClass.DataApi.AddListener(client, this);
    }

    public void OnConnectionSuspended(int reason)
    {
        Android.Util.Log.Error("GMSonnection suspended " + reason, "");
        WearableClass.DataApi.RemoveListener(client, this);
    }

    public void OnConnectionFailed(Android.Gms.Common.ConnectionResult result)
    {
        Android.Util.Log.Error("GMSonnection failed " + result.ErrorCode, "");
    }


    protected override void OnStop()
    {
        base.OnStop();
        client.Disconnect();
    }

    public void OnMessageReceived(IMessageEvent messageEvent)
    {
        if (messageEvent.Path.Equals(_syncPath))
        {
            var msg = System.Text.Encoding.UTF8.GetString(messageEvent.GetData());

            this.RunOnUiThread(() =>
                Android.Widget.Toast.MakeText(this, msg, ToastLength.Long).Show());
        }
    }

    public void ProcessMessage(Intent intent) 
    {
        if (intent.GetStringExtra("Device") != device)
        {
            text = intent.GetStringExtra("WearMessage");
            //do stuffs here

        }
    }

    public void SendData() {
        try {
            var request = PutDataMapRequest.Create(_syncPath);
            var map = request.DataMap;
            map.PutString("Device", device);
            map.PutString("Message", "Xamarin Forms says Hello from Wearable!");
            map.PutLong("UpdatedAt", DateTime.UtcNow.Ticks);
            WearableClass.DataApi.PutDataItem(_client, request.AsPutDataRequest());
        }
        finally {
            _client.Disconnect();
        }

}

In your Phone application change the static string device to Phone and change the message text if you want to:

    static string device = "Phone";

            map.PutString("Message", "Xamarin Forms says Hello from Phone!");

Then add the WearService class to both your Android Projects add the same usings as added to the MAinActivity an change the Wearservice as follows:

[Service]
[IntentFilter(new[] { "com.google.android.gms.wearable.BIND_LISTENER" })]
public class WearService : WearableListenerService
{
    const string _syncPath = "/KorfballTimer/Data";
    GoogleApiClient _client;

    public override void OnCreate()
    {
        base.OnCreate();
        _client = new GoogleApiClient.Builder(this.ApplicationContext)
                .AddApi(WearableClass.API)
                .Build();

        _client.Connect();

        Android.Util.Log.Info("WearIntegrationreated", "");
    }

    public override void OnDataChanged(DataEventBuffer dataEvents) 
    {
        var dataEvent = Enumerable.Range(0, dataEvents.Count)
                                  .Select(i => dataEvents.Get(i).JavaCast<IDataEvent)
                                  .FirstOrDefault(x => x.Type == DataEvent.TypeChanged && x.DataItem.Uri.Path.Equals(_syncPath));
        if (dataEvent == null)
            return;

        //get data from wearable
        var dataMapItem = DataMapItem.FromDataItem(dataEvent.DataItem);
        var map = dataMapItem.DataMap;
        string message = dataMapItem.DataMap.GetString("Message");

        Intent intent = new Intent();
        intent.SetAction(Intent.ActionSend);
        intent.PutExtra("WearMessage", message);
        intent.PutExtra("Device", map.GetString("Device"));
        LocalBroadcastManager.GetInstance(this).SendBroadcast(intent);
    }
}

And finally, Add the meta data in the AndroidManifest.xml under element:

    <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />

If you don't want the IOS and Windows projects in your Wear application, just delete them. Now you can build your Wear application in Xamarin Forms just as you do with your phone application. Happy Coding.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM