com.google.android.apps.dashclock.api.ExtensionData Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dashclock-api Show documentation
Show all versions of dashclock-api Show documentation
The DashClock API allows you to extend DashClock with custom extensions
The newest version!
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.apps.dashclock.api;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import org.json.JSONException;
import org.json.JSONObject;
import java.net.URISyntaxException;
/**
* A parcelable, serializable object representing data related to a {@link DashClockExtension} that
* should be shown to the user.
*
*
* This class follows the fluent
* interface style, using method chaining to provide for more readable code. For example, to set
* the status and visibility of this data, use {@link #status(String)} and {@link #visible(boolean)}
* methods like so:
*
*
* ExtensionData data = new ExtensionData();
* data.visible(true).status("hello");
*
*
* Conversely, to get the status, use {@link #status()}. Setters and getters are thus overloads
* (or overlords?) of the same method.
*
* Required fields
*
* While no fields are required, if the data is 'visible' (i.e. {@link #visible(boolean)} has been
* called with true
, at least the following fields should be populated:
*
*
* - {@link #icon(int)}
* - {@link #status(String)}
*
*
* Really awesome extensions will also set these fields:
*
*
* - {@link #expandedTitle(String)}
* - {@link #expandedBody(String)}
* - {@link #clickIntent(android.content.Intent)}
*
*
* @see DashClockExtension#publishUpdate(ExtensionData)
*/
public class ExtensionData implements Parcelable {
/**
* Since there might be a case where new versions of DashClock use extensions running
* old versions of the protocol (and thus old versions of this class), we need a versioning
* system for the parcels sent between the core app and its extensions.
*/
public static final int PARCELABLE_VERSION = 2;
private static final String KEY_VISIBLE = "visible";
private static final String KEY_ICON = "icon";
private static final String KEY_ICON_URI = "icon_uri";
private static final String KEY_STATUS = "status";
private static final String KEY_EXPANDED_TITLE = "title";
private static final String KEY_EXPANDED_BODY = "body";
private static final String KEY_CLICK_INTENT = "click_intent";
private static final String KEY_CONTENT_DESCRIPTION = "content_description";
/**
* The maximum length for {@link #status(String)}. Enforced by {@link #clean()}.
*/
public static final int MAX_STATUS_LENGTH = 32;
/**
* The maximum length for {@link #expandedTitle(String)}. Enforced by {@link #clean()}.
*/
public static final int MAX_EXPANDED_TITLE_LENGTH = 100;
/**
* The maximum length for {@link #expandedBody(String)}. Enforced by {@link #clean()}.
*/
public static final int MAX_EXPANDED_BODY_LENGTH = 1000;
/**
* The maximum length for {@link #contentDescription(String)}. Enforced by {@link #clean()}.
*/
public static final int MAX_CONTENT_DESCRIPTION_LENGTH = 32 +
MAX_STATUS_LENGTH + MAX_EXPANDED_TITLE_LENGTH + MAX_EXPANDED_BODY_LENGTH;
private boolean mVisible = false;
private int mIcon = 0;
private Uri mIconUri = null;
private String mStatus = null;
private String mExpandedTitle = null;
private String mExpandedBody = null;
private Intent mClickIntent = null;
private String mContentDescription = null;
public ExtensionData() {
}
/**
* Returns whether or not the relevant extension should be visible (whether or not there is
* relevant information to show to the user about the extension). Default false.
*/
public boolean visible() {
return mVisible;
}
/**
* Sets whether or not the relevant extension should be visible (whether or not there is
* relevant information to show to the user about the extension). Default false.
*/
public ExtensionData visible(boolean visible) {
mVisible = visible;
return this;
}
/**
* Returns the ID of the drawable resource within the extension's package that represents this
* data. Default 0.
*/
public int icon() {
return mIcon;
}
/**
* Sets the ID of the drawable resource within the extension's package that represents this
* data. The icon should be entirely white, with alpha, and about 48x48 dp. It will be
* scaled down as needed. If there is no contextual icon representation of the data, simply
* use the extension or app icon. If an {@link #iconUri(Uri) iconUri} is provided, it
* will take precedence over this value. Default 0.
*
* @see #iconUri(Uri)
*/
public ExtensionData icon(int icon) {
mIcon = icon;
return this;
}
/**
* Returns the content:// URI of a bitmap representing this data. Default null.
*
* @since Protocol Version 2 (API r2.x)
*/
public Uri iconUri() {
return mIconUri;
}
/**
* Sets the content:// URI of the bitmap representing this data. This takes precedence over
* the regular {@link #icon(int) icon resource ID} if set. This resource will be loaded
* using {@link android.content.ContentResolver#openFileDescriptor(android.net.Uri, String)} and
* {@link android.graphics.BitmapFactory#decodeFileDescriptor(java.io.FileDescriptor)}. See the
* {@link #icon(int) icon} method for guidelines on the styling of this bitmap.
*
* @since Protocol Version 2 (API r2.x)
*/
public ExtensionData iconUri(Uri iconUri) {
mIconUri = iconUri;
return this;
}
/**
* Returns the short string representing this data, to be shown in DashClock's collapsed form.
* Default null.
*/
public String status() {
return mStatus;
}
/**
* Sets the short string representing this data, to be shown in DashClock's collapsed form.
* Should be no longer than a few characters. For example, if your {@link #expandedTitle()} is
* "45°, Sunny", your status could simply be "45°". Alternatively, if the status contains a
* single newline, DashClock may break it up over two lines and use a smaller font. This should
* be avoided where possible in favor of an {@link #expandedTitle(String)}. Default null.
*/
public ExtensionData status(String status) {
mStatus = status;
return this;
}
/**
* Returns the expanded title representing this data. Generally a longer form of
* {@link #status()}. Default null.
*/
public String expandedTitle() {
return mExpandedTitle;
}
/**
* Sets the expanded title representing this data. Generally a longer form of
* {@link #status()}. Can be multiple lines, although DashClock will cap the number of lines
* shown. If this is not set, DashClock will just use the {@link #status()}.
* Default null.
*/
public ExtensionData expandedTitle(String expandedTitle) {
mExpandedTitle = expandedTitle;
return this;
}
/**
* Returns the expanded body text representing this data. Default null.
*/
public String expandedBody() {
return mExpandedBody;
}
/**
* Sets the expanded body text (below the expanded title), representing this data. Can span
* multiple lines, although DashClock will cap the number of lines shown. Default null.
*/
public ExtensionData expandedBody(String expandedBody) {
mExpandedBody = expandedBody;
return this;
}
/**
* Returns the click intent to start (using
* {@link android.content.Context#startActivity(android.content.Intent)}) when the user clicks
* the status in DashClock. Default null.
*/
public Intent clickIntent() {
return mClickIntent;
}
/**
* Sets the click intent to start (using
* {@link android.content.Context#startActivity(android.content.Intent)}) when the user clicks
* the status in DashClock. The activity represented by this intent will be started in a new
* task and should be exported. Default null.
*/
public ExtensionData clickIntent(Intent clickIntent) {
mClickIntent = clickIntent;
return this;
}
/**
* Returns the content description for this data, used for accessibility purposes.
*
* @since Protocol Version 2 (API r2.x)
*/
public String contentDescription() {
return mContentDescription;
}
/**
* Sets the content description for this data. This content description will replace the
* {@link #status()}, {@link #expandedTitle()} and {@link #expandedBody()} for accessibility
* purposes.
*
* @see android.view.View#setContentDescription(CharSequence)
* @since Protocol Version 2 (API v2.x)
*/
public ExtensionData contentDescription(String contentDescription) {
mContentDescription = contentDescription;
return this;
}
/**
* Serializes the contents of this object to JSON.
*/
public JSONObject serialize() throws JSONException {
JSONObject data = new JSONObject();
data.put(KEY_VISIBLE, mVisible);
data.put(KEY_ICON, mIcon);
data.put(KEY_ICON_URI, (mIconUri == null ? null : mIconUri.toString()));
data.put(KEY_STATUS, mStatus);
data.put(KEY_EXPANDED_TITLE, mExpandedTitle);
data.put(KEY_EXPANDED_BODY, mExpandedBody);
data.put(KEY_CLICK_INTENT, (mClickIntent == null) ? null : mClickIntent.toUri(0));
data.put(KEY_CONTENT_DESCRIPTION, mContentDescription);
return data;
}
/**
* Deserializes the given JSON representation of extension data, populating this
* object.
*/
public void deserialize(JSONObject data) throws JSONException {
this.mVisible = data.optBoolean(KEY_VISIBLE);
this.mIcon = data.optInt(KEY_ICON);
String iconUriString = data.optString(KEY_ICON_URI);
this.mIconUri = TextUtils.isEmpty(iconUriString) ? null : Uri.parse(iconUriString);
this.mStatus = data.optString(KEY_STATUS);
this.mExpandedTitle = data.optString(KEY_EXPANDED_TITLE);
this.mExpandedBody = data.optString(KEY_EXPANDED_BODY);
try {
this.mClickIntent = Intent.parseUri(data.optString(KEY_CLICK_INTENT), 0);
} catch (URISyntaxException ignored) {
}
this.mContentDescription = data.optString(KEY_CONTENT_DESCRIPTION);
}
/**
* Serializes the contents of this object to a {@link Bundle}.
*/
public Bundle toBundle() {
Bundle data = new Bundle();
data.putBoolean(KEY_VISIBLE, mVisible);
data.putInt(KEY_ICON, mIcon);
data.putString(KEY_ICON_URI, (mIconUri == null ? null : mIconUri.toString()));
data.putString(KEY_STATUS, mStatus);
data.putString(KEY_EXPANDED_TITLE, mExpandedTitle);
data.putString(KEY_EXPANDED_BODY, mExpandedBody);
data.putString(KEY_CLICK_INTENT, (mClickIntent == null) ? null : mClickIntent.toUri(0));
data.putString(KEY_CONTENT_DESCRIPTION, mContentDescription);
return data;
}
/**
* Deserializes the given {@link Bundle} representation of extension data, populating this
* object.
*/
public void fromBundle(Bundle src) {
this.mVisible = src.getBoolean(KEY_VISIBLE, true);
this.mIcon = src.getInt(KEY_ICON);
String iconUriString = src.getString(KEY_ICON_URI);
this.mIconUri = TextUtils.isEmpty(iconUriString) ? null : Uri.parse(iconUriString);
this.mStatus = src.getString(KEY_STATUS);
this.mExpandedTitle = src.getString(KEY_EXPANDED_TITLE);
this.mExpandedBody = src.getString(KEY_EXPANDED_BODY);
try {
this.mClickIntent = Intent.parseUri(src.getString(KEY_CLICK_INTENT), 0);
} catch (URISyntaxException ignored) {
}
this.mContentDescription = src.getString(KEY_CONTENT_DESCRIPTION);
}
/**
* @see Parcelable
*/
public static final Creator CREATOR
= new Creator() {
public ExtensionData createFromParcel(Parcel in) {
return new ExtensionData(in);
}
public ExtensionData[] newArray(int size) {
return new ExtensionData[size];
}
};
private ExtensionData(Parcel in) {
int parcelableVersion = in.readInt();
int parcelableSize = in.readInt();
int startPosition = in.dataPosition();
if (parcelableVersion >= 1) {
this.mVisible = (in.readInt() != 0);
this.mIcon = in.readInt();
this.mStatus = in.readString();
if (TextUtils.isEmpty(this.mStatus)) {
this.mStatus = null;
}
this.mExpandedTitle = in.readString();
if (TextUtils.isEmpty(this.mExpandedTitle)) {
this.mExpandedTitle = null;
}
this.mExpandedBody = in.readString();
if (TextUtils.isEmpty(this.mExpandedBody)) {
this.mExpandedBody = null;
}
try {
this.mClickIntent = Intent.parseUri(in.readString(), 0);
} catch (URISyntaxException ignored) {
}
}
if (parcelableVersion >= 2) {
this.mContentDescription = in.readString();
if (TextUtils.isEmpty(this.mContentDescription)) {
this.mContentDescription = null;
}
String iconUriString = in.readString();
this.mIconUri = TextUtils.isEmpty(iconUriString) ? null : Uri.parse(iconUriString);
}
// Only advance the data position if the parcelable version is >= 2. In v1 of the
// parcelable, there was an awful bug where the parcelableSize was complete nonsense.
if (parcelableVersion >= 2) {
in.setDataPosition(startPosition + parcelableSize);
}
}
@Override
public void writeToParcel(Parcel parcel, int i) {
/**
* NOTE: When adding fields in the process of updating this API, make sure to bump
* {@link #PARCELABLE_VERSION}.
*/
parcel.writeInt(PARCELABLE_VERSION);
// Inject a placeholder that will store the parcel size from this point on
// (not including the size itself).
int sizePosition = parcel.dataPosition();
parcel.writeInt(0);
int startPosition = parcel.dataPosition();
// Version 1 below
parcel.writeInt(mVisible ? 1 : 0);
parcel.writeInt(mIcon);
parcel.writeString(TextUtils.isEmpty(mStatus) ? "" : mStatus);
parcel.writeString(TextUtils.isEmpty(mExpandedTitle) ? "" : mExpandedTitle);
parcel.writeString(TextUtils.isEmpty(mExpandedBody) ? "" : mExpandedBody);
parcel.writeString((mClickIntent == null) ? "" : mClickIntent.toUri(0));
// Version 2 below
parcel.writeString(TextUtils.isEmpty(mContentDescription) ? "" : mContentDescription);
parcel.writeString(mIconUri == null ? "" : mIconUri.toString());
// Go back and write the size
int parcelableSize = parcel.dataPosition() - startPosition;
parcel.setDataPosition(sizePosition);
parcel.writeInt(parcelableSize);
parcel.setDataPosition(startPosition + parcelableSize);
}
@Override
public int describeContents() {
return 0;
}
@SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
@Override
public boolean equals(Object o) {
if (o == null) {
return false;
}
try {
ExtensionData other = (ExtensionData) o;
return other.mVisible == mVisible
&& other.mIcon == mIcon
&& objectEquals(other.mIconUri, mIconUri)
&& TextUtils.equals(other.mStatus, mStatus)
&& TextUtils.equals(other.mExpandedTitle, mExpandedTitle)
&& TextUtils.equals(other.mExpandedBody, mExpandedBody)
&& objectEquals(other.mClickIntent, mClickIntent)
&& TextUtils.equals(other.mContentDescription, mContentDescription);
} catch (ClassCastException e) {
return false;
}
}
private static boolean objectEquals(Object x, Object y) {
if (x == null || y == null) {
return x == y;
} else {
return x.equals(y);
}
}
/**
* Returns true if the two provided data objects are equal (or both null).
*/
public static boolean equals(ExtensionData x, ExtensionData y) {
if (x == null || y == null) {
return x == y;
} else {
return x.equals(y);
}
}
@Override
public int hashCode() {
throw new UnsupportedOperationException();
}
/**
* Cleans up this object's data according to the size limits described by
* {@link #MAX_STATUS_LENGTH}, {@link #MAX_EXPANDED_TITLE_LENGTH}, etc.
*/
public void clean() {
if (!TextUtils.isEmpty(mStatus)
&& mStatus.length() > MAX_STATUS_LENGTH) {
mStatus = mStatus.substring(0, MAX_STATUS_LENGTH);
}
if (!TextUtils.isEmpty(mExpandedTitle)
&& mExpandedTitle.length() > MAX_EXPANDED_TITLE_LENGTH) {
mExpandedTitle = mExpandedTitle.substring(0, MAX_EXPANDED_TITLE_LENGTH);
}
if (!TextUtils.isEmpty(mExpandedBody)
&& mExpandedBody.length() > MAX_EXPANDED_BODY_LENGTH) {
mExpandedBody = mExpandedBody.substring(0, MAX_EXPANDED_BODY_LENGTH);
}
if (!TextUtils.isEmpty(mContentDescription)
&& mContentDescription.length() > MAX_EXPANDED_BODY_LENGTH) {
mContentDescription = mContentDescription.substring(0, MAX_CONTENT_DESCRIPTION_LENGTH);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy