Skip to content

5. SDK: Creating a Requirement

Kieron Quinn edited this page Oct 23, 2023 · 2 revisions

Creating a Requirement

Smartspacer's Requirements allow the user to customise when Targets and Complications appear on the Smartspace. Plugins can provide their own Requirements based on their own rules.

Setup

Firstly, create a new project or open an existing one. It does not matter if the project uses Compose or Views, since Requirements use neither.

Adding the SDK

Add the Plugin SDK:

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

You can find the latest version here

The Requirement Class

Requirements at their core are Content Providers, but the Smartspacer SDK handles most of the logic required for a Provider to function.

The basic Requirement class is as follows:

class ExampleRequirement: SmartspacerRequirementProvider() {

    override fun isRequirementMet(smartspacerId: String): Boolean {
        //Return whether the Requirement is currently met
    }

    override fun getConfig(smartspacerId: String?): Config {
        //Return your configuration
    }

    override fun onProviderRemoved(smartspacerId: String) {
        //Handle removal of the Requirement (optional)
    }

}

Declaring in the Manifest

Since a Requirement is based on ContentProvider, it must be specified as one, with a specific action and permission:

<provider
	android:name=".ExampleRequirement"
	android:authorities="${applicationId}.requirement.example"
	android:permission="com.kieronquinn.app.smartspacer.permission.ACCESS_SMARTSPACER_REQUIREMENTS"
	android:exported="true">
	<intent-filter>
		<action android:name="com.kieronquinn.app.smartspacer.REQUIREMENT" />
	</intent-filter>
</provider>

Your Requirement must be exported, and must have the specific action and permission shown above. Without this, they will not work in Smartspacer.

Configuration

The getConfig method needs to return some basic information for Smartspacer about your Requirement. The available options are:

Config(
	label = // Your Requirement's label to be shown in Smartspacer's settings UI
	description = // A short description for your Requirement to be shown in the same UI
	icon = // An Icon (android.graphics.drawable.Icon) to show in the same UI
	allowAddingMoreThanOnce = // Optional: Whether the Requirement should be able to be added multiple times at once (defaults to true)
	configActivity = // Optional: An Intent given as an option to the user in the settings UI after adding
	setupActivity = // Optional: An Intent of an Activity presented to the user during setup (see below)
	compatibilityState = // Optional: A CompatibilityState object representing whether this Requirement is compatible with the device or not (defaults to always compatible)
)

As a bare minimum, you must specify a label, description and icon. It's also recommended you consider specifying a Compatibility State, checking whether the device is able to add your Requirement or not. For example, you may require another app to load data from, in which case you would return CompatibilityState.Incompatible("App X is not installed") if the app is not installed, or CompatibilityState.Compatible if it is.

The setup and configuration activities work much the same as those used for App Widgets by Android. Specify an Intent, with any extras you wish, and Smartspacer will open it during the adding of a Requirement or when the user selects the settings option from the UI respectively. These Intents will also automatically be provided with the extra SmartspacerConstants.EXTRA_SMARTSPACER_ID, a String containing the unique ID given to this Requirement during setup.

The setup activity must call Activity.setResult(Activity.RESULT_OK) before finishing to tell Smartspacer setup was successful and to continue adding the Requirement. Otherwise, the Requirement will not be added and the provider's onProviderRemovedMethod will be called.

The configuration activity may call Activity.setResult(Activity.RESULT_OK) before finishing to tell Smartspacer to send a change notification to the Requirement, though this is optional.

When specifying a refresh period, please be mindful of battery use. Refreshes are also not guaranteed, the value is a limit to how often the refresh call will be made, not a guaranteed call. Refreshes also are not guaranteed to happen at the top of the minute (:00), so may not be reliable for time-sensitive refreshes such as calendar events or reminders. Enabling the refreshIfNotVisible option means the refresh call will also be made when your Requirement is not currently in Smartspace, which may be useful if you check for an update to something periodically. Enabling this option can have a severe effect on battery life if combined with refreshing often, so please be extremely careful.

Returning a Requirement's State

Smartspacer will call isRequirementMet with the Requirement's ID when required, either due to a refresh request or if a reload is required by Smartspacer itself. You must return true if the Requirement is met, or false if not. The ID provided to this method is the same unique ID provided during setup, so you can use it to retreive local data to display in a Requirement, but please do not block the thread. Very simple data loading (eg. reading a row from a database or a JSON file) is usually fine, but making a network request at this state may result in failed loads and your Requirement not working.

Requirement Removal

When the user removes a Requirement from Smartspacer, the onProviderRemoved method will be called with your unique Smartspacer ID. You should perform cleanup actions here, such as removing any data from the database related to the Requirement.

Please note that this method will not be called if Smartspacer is uninstalled.

Requesting an reload for your Requirement

If you wish to notify Smartspacer of a change to your Requirement, and thus a call to isRequirementMet should be made, you can call one of the two static notifyChange methods in SmartspacerRequirementProvider from anywhere you have a context. These are:

  • SmartspacerRequirementProvider.notifyChange(Context, Authority, Smartspacer ID (optional)): Updates a Requirement with a given provider Authority. If provided, only that with a given Smartspacer ID will be updated (only useful if you are allowing multiple of the same Requirement to be added)

  • SmartspacerRequirementProvider.notifyChange(Context, Class<out SmartspacerRequirementProvider>, Smartspacer ID (optional)): Updates a Requirement with a given Requirement class. If provided, only that with a given Smartspacer ID will be updated (only useful if you are allowing multiple of the same Requirement to be added)

Requirement Backups

Smartspacer supports an integrated backup system, since it is not available on Google Play. The idea of this is all Targets, Complications and Requirements contribute to a single backup file, which when restored provides the same data back to the plugins for restoration. You should put basic information such as settings in here, but data such as images should not be backed up - and large data will be rejected due to Android's Binder limit of 1MB. Backups are entirely optional - if you do not wish to implement it, the user will simply be presented with your Requirement during restoration and add it as if it were new.

During a backup, the createBackup method will be called, passing the standard Smartspacer ID:

override fun createBackup(smartspacerId: String): Backup {
	val settings = //load settings
	return Backup(settings.serialiseToString(), "A short description")
}

You should use the ID to load the settings of your Requirement, and serialise it to a String (consider using GSON or similar libraries to flatten to JSON easily). A short description should also be provided specifying what was in the backup, which will be shown to the user in lieu of the Requirement's description so they know what is being restored.

Requirement Restoration

Similarly, the restoreBackup method is called during the restoration process:

override fun restoreBackup(smartspacerId: String, backup: Backup): Boolean {
	//Handle restoring your backup
	notifyChange(smartspacerId)
	return true
}

Important note: The Smartspacer ID here will not be the same as your one at the time of backup. Requirements do not keep a static ID between different Smartspacer install instances

The Backup object here contains the data you sent during the backup process. You should deserialise the data, commit it however you wish, and then call notifyChange(smartspacerId) to refresh Requirement with this new data. Returning true tells Smartspacer the restoration was successful, and the setup activity (if specified) does not need to be shown for this Requirement. If you cannot restore the Requirement for whatever reason, return false and the setup activity will be shown.

Note: If your Requirement requires certain permissions which will not persist across a reinstall, consider returning false even if the restore succeeds, so you can make sure the user grants the permission before the Requirement finishes being added.