Skip to main content

Android .NET CPIK Libraries

Contents


NOTE: Microsoft has announced it will end support for Xamarin and Xamarin.Forms on May 1, 2024. The Xamarin.Android and Xamarin.iOS platforms are now integrated directly into .NET 6 and higher. Starting with CoPilot 10.27, we have updated the CPIK libraries and sample applications for .NET Android and .NET iOS. Xamarin is still supported in CoPilot 10.27, but it will not be supported in future versions of CoPilot. To integrate CoPilot using .NET, you will need to use Visual Studio 2022 (version 17.8 or higher) and .NET 8.0 or higher.


Libraries and Assets

You can download and integrate the CPIK libraries files for Android .NET one of two ways:

  • Automated method, using our public repository.
  • Manual method, by logging into our public repository or Partner Portal.

Automated Method

  1. Open your solution in Visual Studio and then select Tools > Options. Scroll down to NuGet Package Manager, expand it, and then select Package Sources. Click on the plus icon to add a new source.

  2. In the Name field, enter a name (ex: trimble-maps) and in the Source field, enter this url: https://trimblemaps.jfrog.io/artifactory/api/nuget/dotnet. Make sure the checkbox is checked and hit OK.

  3. Go to Tools > NuGet Package Manager > Manage NuGet Packages For Solution. Now select the name you chose in Step 2 in the Package source dropdown box. After about a minute, on the left side of the pane, you should see one package appear named cpik. If it is not installed, select it and hit Install. If it is already installed, check if any new update is available in the update section. Once the install is complete, you should see CPIKWrapper has been added under your project References.

Manual Method

You can manually download the files:

  • As NuGet packages in our public repository: https://trimblemaps.jfrog.io/ui/native/dotnet/.
  • As a .zip file in our Partner Portal: https://c2.trimblemaps.com/. After downloading and extracting the CoPilot CPIK libraries .zip file from the partner portal, you should have access to everything needed to get up and running quickly. The CPIK libraries package includes the following:

Sample App

  • Source code of the sample application
  • Sample application APK that can be install on an Android device

Libraries & Resources

  • The CPIKWrapper.dll is the biggest change from Xamarin to .NET as now the CPIKWrapper.dll does not include the android libraries and assets. It is just a wrapper for the java code.
  • Libs/CPIKWrapper.dll: The customer application shall use this library to integrate CPIK libraries.
  • Libs/cpiklibraryaar-release.aar: Android archive containing all the libraries and assets needed.
  • Native Libs/. : The native library need to place a libs folder in the customer application (optional).
  • Asset: Need to copy to customer’s application asset folder (optional)
  • AndroidManifest_template.xml: Reference AndroidManifest.xml file.

Note: When downloading files, be sure to download the latest version of CoPilot and to only include files from that version in your project. You use the CPIK API calls the same way as you did in Xamarin.

Sample App

Source Code

  1. Open the CPIKFragment_NET.sln from Sample App/Source Code/CPIKFragment_NET.sln in Visual Studio 2022.
  2. If you want to run the app using the CPIK_JavaBindings project, hit Run and let the application build. The FragmentApp.csproj is set up for CPIK_JavaBindings.
  3. If you want to manually reference the cpiklibraryaar-release.aar and CPIKWrapper.dll in the FragmentApp.csproj, you need to do it following the steps below. This can apply to your own application as well, not just the fragment app. Add code below to the FragmentApp.csproj. It adds the cpiklibraryaar-release.aar and CPIKWrapper.dll reference to the application. It contains all the information required to run (i.e. library, asset, resources, component, etc.)
<ItemGroup>
    <AndroidLibrary Remove="cpiklibraryaar-release.aar" />
  </ItemGroup>
  <ItemGroup>
    <None Remove="cpiklibraryaar-release.aar" />
  </ItemGroup>
<ItemGroup>
    <Reference Include="CPIKWrapper">
      <HintPath>..\..\Library and Resource\libs\CPIKWrapper.dll</HintPath>
    </Reference>
  </ItemGroup>
  1. You can now run the app: com.alk.copilot_generic_enterprise_NET_fragment.apk Sample APK which can be installed on CoPilot

CPIK Java Bindings

The CPIK Java Bindings project is a Java library bindings project that creates a wrapper, CPIKWrapper.dll, for CPIK. However, in .NET Android, the .aar files are no longer embedded in the dll. This means that in order to use the CPIKWrapper.dll, we need the cpiklibraryaar-release.aar alongside it.

The CPIK Java Bindings is a separate project that can nicely separate this task for you. Your application, like our fragment app, would reference this project and be able to use the CPIK APIs. This is an example of a nicer, cleaner, and more organized way to add CPIK to your application. Of course, you can simply add references to both the CPIKWrapper.dll and cpiklibraryaar-release.aar in your .csproj like in the code snippet above.

Libraries & Resources

  • Libs /CPIKWrapper.dll: This is a binding library to interface with CoPilot. The CPIK libraries jar files are no longer wrapped into CPIKWrapper.dll. You will need to add the cpiklibraryaar-release.aar to your application.
  • Libs/cpiklibraryaar-release.aar.: This is the aar containing all the assets, native libs and CPIK jar files that are needed.
  • Native libs/. (Optional): This is the native library used by CoPilot. Copy the native library to your project. Copy all the folders under Library and Resource/native libs/. to /lib/. to your project. It contains native libraries for different architectures required to run CoPilot. If you have an existing lib folder to your project, merge the content. To deploy a native library with a .NET Android application, add the library binary to the project and set its Build Action to AndroidNativeLibrary.
  • Asset (Optional): Copy the Library and Resource/assets folder into your project. Copy the entire assets folder located under Library and Resource/assets/ to /assets/ folder to your project. It contains vital information to run CoPilot (e.g. theme, config files, skin, etc.). If you have an existing assets folder in your project, merge the content. Make sure that the entire Copilot folder under Assets is added to .NET and set BuildAction to AndroidAsset. If you want to add your own product.cfg to your project, then you can create a /assets/copilot/product.cfg in your project. This product.cfg will be added to your application for CoPilot.
  • AndroidManifest_template.xml: This file contains all required attributes and permission to run the CoPilot. You need to merge this information to your existing AndroidManifest.xml.

Library Integration instructions

Setup instructions:

  1. Integrate the contents of Library & Resource/AndroidManifest_template.xml into your application’s AndroidManifest.xml.
  2. You can either reference the CPIK_JavaBindings bindings project that contains the cpiklibraryaar-release.aar and will create a CPIKWrapper for it or you can manually reference these two objects to your project. If you choose to reference the CPIK_JavaBindings project, then you are done and can use CPIK! Otherwise move to step 3. This is the recommended way to do it because the way .NET creates .dll wrappers now does not embed the CPIK library inside the .dll so the process is tricker.
  3. Reference Library & Resource/libs/ CPIKWrapper.dll that contains the C# wrapper for the jar files.
  4. Add Library & Resource/libs/cpiklibraryaar-release.aar to your project and set the BuildAction to Android Library. If you see some build error related to CPIK, you might need to edit your .csproj manually. Refer to the code block above.
  5. Develop! Use the included sample application as a guide.

API Documentation

  • The API documentation detailed on this site references the classes and their members. It also contains the signature of each member of the classes including description and usage.

  • The documentation contains syntax for Java. Developers can easily map the relevant syntax with C# in Xamarin. You can also check the syntax in Xamarin by exploring CPIKWrapper.dll by adding reference in project.

Sample Application

This sample application is meant to demonstrate how to properly integrate CoPilot into your application. If you’re just getting started, take a look at MainActivity.cs. This is the application’s launcher activity. It demonstrates how to bind to the CoPilot service and how to show the service’s associated view either through a fragment (see CustomFragment.cs) or by simply putting the view on the screen. After binding to the service, you will receive a callback (onCPStartup()) from CoPilot that indicates that the library is ready to receive API calls.

Most of our APIs are demonstrated in the sample app via various fragment classes that each correspond to a different API module. These fragments are controlled by ApiMainActivity.cs. If you have a question about how to use a specific API, look at the module’s fragment to see how to use it properly.

CoPilot Appearing very Dim

It has been noted that in some circumstances the CoPilot UI can appear significantly darker/dimmer than the integrated application. This can be due to the styles.xml settings and easily resolved by adding the following to your styles.xml:

<style name="MyTheme" parent="Theme.AppCompat.Light.NoActionBar">
  <item name="android:backgroundDimEnabled">false</item>
</style>

Binding the Service

Add the following to your MainActivity.cs:

#region IServiceConnection

        public void OnServiceConnected(ComponentName name, IBinder service)
        {
            Binder = service.JavaCast<CopilotService.CopilotBinder>();
            if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
            {
                Binder.StartForeground(FOREGROUND_SERVICE_ID, GetNotificationForService());
            }
            else
            {
                NotificationCompat.Builder builder = new NotificationCompat.Builder(mSelfRef);
                builder.SetContentTitle("Hello World");

                Intent resultIntent = new Intent(this, typeof(MainActivity));
                Android.App.PendingIntent pendingIntent = Android.App.PendingIntent.GetActivity(this, 0, resultIntent, Android.App.PendingIntentFlags.UpdateCurrent | Android.App.PendingIntentFlags.Mutable);
                builder.SetContentIntent(pendingIntent);

                Binder.StartForeground(FOREGROUND_SERVICE_ID, builder.Build());
            }
        }

        public void OnServiceDisconnected(ComponentName name)
        {

        }

        [Android.Annotation.TargetApi(Value = 26)]
        private Android.App.Notification GetNotificationForService()
        {
            Android.App.NotificationManager notificationManager = (Android.App.NotificationManager)ApplicationContext.GetSystemService(Context.NotificationService);
            string channelName = Resources.GetString(Resource.String.app_name);
            Android.App.NotificationChannel notificationChannel = new Android.App.NotificationChannel(NOTIFICATION_CHANNEL, channelName, Android.App.NotificationImportance.Default);
            notificationChannel.Description = channelName;
            notificationChannel.SetSound(null, null);

            notificationManager.CreateNotificationChannel(notificationChannel);
            Intent resultIntent = new Intent(ApplicationContext, typeof(MainActivity));
            Android.App.PendingIntent pendingIntent = Android.App.PendingIntent.GetActivity(ApplicationContext, 0, resultIntent, Android.App.PendingIntentFlags.UpdateCurrent | Android.App.PendingIntentFlags.Mutable);

            return new Android.App.Notification.Builder(ApplicationContext, NOTIFICATION_CHANNEL)
                    .SetContentTitle(channelName)
                    .SetOngoing(true)
                    .SetChannelId(NOTIFICATION_CHANNEL)
                    .SetContentIntent(pendingIntent)
                    .SetSmallIcon(Resource.Drawable.alk_icon_launcher)
                    .Build();
        }

        #endregion

The above code creates a new ServiceConnection and overrides two of its methods, the most important one being onServiceConnected(). The code triggers the foreground service and binds CoPilot once the startService() method is called. You’ll also notice the code checks for the Android version. In Android 8.1 and above, Android changed the way it handles services and as a result we have to create a custom notification channel for our service in this case.

Now that we have our core functionality set up for creating and binding the CoPilot service, we need to ensure we have the necessary permissions before doing so.

Validating Permissions

Add the following code into your MainActivity.java:

/**
 * Checks each permission to ensure the application has access. Only really need to check the
 * more 'intrusive' permissions like location, but left all in to be consistent with
 * AndroidManifest.xml
 * @return
 */
private void CheckRequiredPermissions()
        {
            mSelfRef.RequestPermissions(new String[]{
                // Network
                Manifest.Permission.Internet,
                Manifest.Permission.AccessNetworkState,
                // Access external storage
                Manifest.Permission.WriteExternalStorage,
                Manifest.Permission.ReadExternalStorage,
                Manifest.Permission.ManageExternalStorage,
                // Location
                Manifest.Permission.AccessFineLocation,
				// Contacts
				Manifest.Permission.ReadContacts,

				Build.VERSION.SdkInt >= BuildVersionCodes.M ? Manifest.Permission.AccessNotificationPolicy : Manifest.Permission.Internet,

                // System settings
                Build.VERSION.SdkInt <= BuildVersionCodes.LollipopMr1 ? Manifest.Permission.ReadPhoneState : Manifest.Permission.AccessWifiState,

                Manifest.Permission.AccessWifiState,
                Manifest.Permission.WakeLock
            }, MY_PERMISSIONS_REQUEST);

        }
        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
        {
            bool bIsPermissionFailed = false;
            if (requestCode == MY_PERMISSIONS_REQUEST && grantResults.Length > 0)
            {
                foreach (Permission permission in grantResults)
                {
                    if (permission != Permission.Granted)
                    {
                        bIsPermissionFailed = true;
                        break;
                    }
                }
            }
            if (bIsPermissionFailed)
            {
            }

            StartCopilot();

        }

With the above we have two main functions:

  1. CheckRequiredPermissions() requests permissions from the user if it needs to, otherwise just starts the setup procedure of CoPilot. We will write the StartCopilot() method later.
  2. We override the OnRequestPermissionsResult() function. When the user grants or denies access when requested, they will return here.

Controlling CoPilot’s Startup

At this point, the core code for starting the service is written, and the app has the permissions it needs to work with CoPilot itself. In this section, we’re going to write a few listeners and hooks to dictate what CoPilot does on its start up. We’ll begin by licensing CoPilot when it starts up, and then ensure the software is ready before interacting with it further. Start by adding the following code to you MainActivity.cs:

public static final String AMS_USERNAME = "YOUR_USERNAME";
public static final String AMS_COMPANYID = "YOUR_COMPANY_ID";
private class MyLicenseHook : LicenseHookListener  {
    public override void OnLicenseMgtLogin(LicenseActivationResponse activationStatus, LicenseMgtInfo loginInfo) {
        // This is fired when we have an activation response back from the licensing system
        base.OnLicenseMgtLogin(activationStatus, loginInfo);
    }
    public override LicenseMgtInfo licenseMgtCredentialHook() {
        // This is fired on launch when registered as a hook, it either logs the user in as
        // a new user or if the user is already logged in with these credentials, does nothing.
        return new LicenseMgtInfo(AMS_USERNAME, AMS_COMPANYID);
    }
}


private class StartupListener : CopilotListener {

    public override void OnCPStartup() {
        SignalCopilotisReady();
    }
}

The above are two private classes that extend some of CoPilot’s listeners. The LicenseHookListener is used to license and activate CoPilot. The licenseMgtCredentialHook() method is fired on launch and effectively provides the username and company ID to authenticate. If you’re unfamiliar with Trimble Maps Account Manager licensing, please speak to your account manager. Additionally, there is a StartupListener that will fire onCPStartup() when CoPilot is ready to be interacted with. There’s a functionside that has not been written yet. We’ll get to that shortly. Now that the listeners and hooks are in place, it’s time to register them. This should be done before the service is started to ensure they fire during the startup calls. This helps control your application’s workflow. Add the following to your MainActivity.cs:

public void RegisterStartupListeners() {
    LicenseListener.RegisterHook(new LicenseHookListener());
    LicenseListener.RegisterListener(new LicenseHookListener());
    CopilotListener.RegisterListener(new StartupListener());
}

Lastly, add the CheckRequiredPermissions() function to your onCreate() for the activity to start the whole process.

@Override
protected void OnCreate(Bundle savedInstanceState) {
    base.OnCreate(savedInstanceState);
    SetContentView(R.Layout.activity_main);


    CheckRequiredPermissions();
}

With that the CoPilot service, licensing and startup procedures are all running. However, they’re running silently in the background—nothing is being displayed yet.

Display CoPilot

Inside your main.axml file, or whichever layout file you use to create a view that effectively fills the entire activity, add:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/apiRootMainLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
     <!-- Container for the copilot fragment -->
      <RelativeLayout
          android:id="@+id/copilot_fragment_container"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:layout_alignParentBottom="true" />
</RelativeLayout>

For the example above, we want CoPilot to take as much of the screen as is available to it. In your application, you may prefer to take a portion of the screen or even rework all the code written above into a fragment instead. For now, we’ll stick to CoPilot taking the entire view. Once the XML is complete, it’s time to write a function to display CoPilot. Add the following to the class that will display CoPilot:

private override void OnCreate(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
     View copilotView = CopilotMgr.View;


            copilotView.RequestFocus();
            return copilotView;

}
Last updated February 5, 2024.