Skip to content

9. SDK: Client SDK (for Launchers)

Kieron Quinn edited this page Nov 13, 2023 · 5 revisions

The Client SDK

The Smartspacer Client SDK allows you to provide a near-native experience of Smartspacer within your launcher or similar app. It functions almost identically to Native Smartspace, and will display all of Smartspacer's Targets and Complications, including those from At a Glance, if the user has specified that. This allows your launcher to gain functionality previously only allowed to the Pixel Launcher or other system-embedded launchers.

Setup

This page assumes you already have a launcher app or similar set up. The Client SDK uses Views, but these can be added via Compose's AndroidView if required.

Requirements

The Smartspacer SDK requires Android API 29+, the same as Smartspacer itself. If your app has a minimum below this, you should make changes to only load the XML containing Smartspacer's Views and only call SmartspacerClient on compatible Android versions. You may also need to use the tools:overrideLibrary attribute on your uses-sdk tags in your manifest, if applicable.

Adding the SDK

Add the Plugin SDK:

implementation 'com.kieronquinn.smartspacer:sdk-client:version'

You can find the latest version here

Permissions

The Smartspacer Client SDK requires a permission in the manifest:

<uses-permission android:name="com.kieronquinn.app.smartspacer.permission.ACCESS_SMARTSPACER"/>

Proguard

If you are using Proguard, exclude Smartspacer's SDK:

# Keep Smartspacer's client SDK
-keep class com.kieronquinn.app.smartspacer.sdk.**  { *; }

Adding Smartspacer's Views

The Smartspacer Client SDK is designed to be a drop-in replacement for the "BcSmartspaceView" seen in may decompiled Pixel Launcher based launchers, or the QSB search fragment in Launcher3. Adding Smartspacer's Views is easy, simply add this View structure to a layout:

<com.kieronquinn.app.smartspacer.sdk.client.views.BcSmartspaceView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/bc_smartspace_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_vertical">

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/smartspace_card_pager"
        android:layout_width="match_parent"
        android:layout_gravity="center" />

    <com.kieronquinn.app.smartspacer.sdk.client.views.PageIndicator
        android:id="@+id/smartspace_page_indicator"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|left|center_vertical|center_horizontal|center|start"
        android:importantForAccessibility="no"
        android:visibility="visible" />

</com.kieronquinn.app.smartspacer.sdk.client.views.BcSmartspaceView>

In the Activity hosting the layout (or the Fragment if it's in a Fragment), close the client when it is destroyed

override fun onDestroy() {
   SmartspacerClient.close()
}

Note: It is safe to call this method regardless of whether the client was created, as it will only close an existing client.

And that's it!

Optional: Customising the Popup

By default, the Client SDK provides its own popup when a Target is clicked. This uses the popular Balloon library. If you wish to provide your own popup, for looks or if you simply do not want to add another dependency, simply exclude the dependency:

implementation('com.kieronquinn.smartspacer:sdk-client:version') {
   exclude group: "com.github.skydoves", module: "balloon"
}

You will also need to provide your own PopupFactory, setting it on the BcSmartspaceView View:

smartspaceView.popupFactory = object: PopupFactory {
	
	override fun createPopup(
        context: Context,
        anchorView: View,
        target: SmartspaceTarget,
        backgroundColor: Int,
        textColour: Int,
        launchIntent: (Intent?) -> Unit,
        dismissAction: ((SmartspaceTarget) -> Unit)? = null,
        aboutIntent: Intent?,
        feedbackIntent: Intent?,
        settingsIntent: Intent?
    ): Popup {
	//Show your own popup view, which implements the Popup interface to allow dismissal
    }

}

Refer to the SDK's default implementation source code to understand what you are required to show in your popup, and what to do with the arguments.

Optional: Getting a Permission IntentSender

By default, the Smartspacer Client SDK will display a permission prompt as a Target in the Smartspace, which when tapped will open Smartspacer's permission grant prompt. After granting, Targets will be populated automatically in the Smartspace.

However, if you want to proactively prompt the user for permission, you can use SmartspacerClient:

val client = SmartspacerClient.getInstance(context)
if(!client.checkCallingPermission()){
   //User has not granted Smartspacer permission
   val intentSender = client.createPermissionRequestIntentSender()
   if(intentSender != null) {
      context.startIntentSenderForResult(...)
   }
}

Note: A null response from SmartspacerClient indicates it is not connected.

Optional: Replacing the Google Discover Feed with Expanded Smartspace

Smartspacer contains a Launcher Overlay Client which displays Expanded Smartspace. If your launcher displays the Discover Feed (also known as Minus One), you can replace it with Expanded Smartspace with just one line of change:

Intent("com.android.launcher3.WINDOW_OVERLAY").apply {
   `package` = "com.google.android.googlequicksearchbox`
   ... 
}

Becomes

Intent("com.android.launcher3.WINDOW_OVERLAY").apply {
   `package` = SmartspacerConstants.SMARTSPACER_PACKAGE_NAME
   ... 
}

Of course, you should probably provide the user with an option between the two, or even open it out to any Launcher Overlay Clients, as a few custom ones exist nowadays - not just Smartspacer!

Contributions

The Smartspacer Client SDK contains layout code derived from Lawnchair, under the Apache 2.0 Licence