blob: 9f2daeab0904d8c2fa851bfc09c5f6a7fc78dc33 [file] [log] [blame]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* 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 android.health.connect.datatypes;
import static android.health.connect.datatypes.validation.ValidationUtils.validateIntDefValue;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.time.Instant;
import java.util.Objects;
import java.util.Set;
/** Set of shared metadata fields for {@link Record} */
public final class Metadata {
/** Unknown recording method. */
public static final int RECORDING_METHOD_UNKNOWN = 0;
/**
* For actively recorded data by the user.
*
* <p>For e.g. An exercise session actively recorded by the user using a phone or a watch
* device.
*/
public static final int RECORDING_METHOD_ACTIVELY_RECORDED = 1;
/**
* For passively recorded data by the app.
*
* <p>For e.g. Steps data recorded by a watch or phone without the user starting a session.
*/
public static final int RECORDING_METHOD_AUTOMATICALLY_RECORDED = 2;
/**
* For manually entered data by the user.
*
* <p>For e.g. Nutrition or weight data entered by the user.
*/
public static final int RECORDING_METHOD_MANUAL_ENTRY = 3;
/**
* Valid set of values for this IntDef. Update this set when add new type or deprecate existing
* type.
*
* @hide
*/
public static final Set<Integer> VALID_TYPES =
Set.of(
RECORDING_METHOD_UNKNOWN,
RECORDING_METHOD_ACTIVELY_RECORDED,
RECORDING_METHOD_AUTOMATICALLY_RECORDED,
RECORDING_METHOD_MANUAL_ENTRY);
private final Device mDevice;
private final DataOrigin mDataOrigin;
private final Instant mLastModifiedTime;
private final String mClientRecordId;
private final long mClientRecordVersion;
@RecordingMethod private final int mRecordingMethod;
private String mId;
/**
* @param device Optional client supplied device information associated with the data.
* @param dataOrigin Where the data comes from, such as application information originally
* generated this data. When {@link Record} is created before insertion, this contains a
* sentinel value, any assigned value will be ignored. After insertion, this will be
* populated with inserted application.
* @param id Unique identifier of this data, assigned by the Android Health Platform at
* insertion time. When {@link Record} is created before insertion, this takes a sentinel
* value, any assigned value will be ignored.
* @param lastModifiedTime Automatically populated to when data was last modified (or originally
* created). When {@link Record} is created before inserted, this contains a sentinel value,
* any assigned value will be ignored.
* @param clientRecordId Optional client supplied record unique data identifier associated with
* the data. There is guaranteed a single entry for any type of data with same client
* provided identifier for a given client. Any new insertions with the same client provided
* identifier will either replace or be ignored depending on associated {@code
* clientRecordVersion}. @see clientRecordVersion
* @param clientRecordVersion Optional client supplied version associated with the data. This
* determines conflict resolution outcome when there are multiple insertions of the same
* {@code clientRecordId}. Data with the highest {@code clientRecordVersion} takes
* precedence. {@code clientRecordVersion} starts with 0. @see clientRecordId
* @param recordingMethod Optional client supplied data recording method to help to understand
* how the data was recorded.
*/
private Metadata(
Device device,
DataOrigin dataOrigin,
String id,
Instant lastModifiedTime,
String clientRecordId,
long clientRecordVersion,
@RecordingMethod int recordingMethod) {
validateIntDefValue(recordingMethod, VALID_TYPES, RecordingMethod.class.getSimpleName());
mDevice = device;
mDataOrigin = dataOrigin;
mId = id;
mLastModifiedTime = lastModifiedTime;
mClientRecordId = clientRecordId;
mClientRecordVersion = clientRecordVersion;
mRecordingMethod = recordingMethod;
}
/**
* @return Client record ID if set, null otherwise
*/
@Nullable
public String getClientRecordId() {
return mClientRecordId;
}
/**
* @return Client record version if set, 0 otherwise
*/
public long getClientRecordVersion() {
return mClientRecordVersion;
}
/**
* @return Corresponds to package name if set. If no data origin is set {@code
* getDataOrigin().getPackageName()} will return null
*/
@NonNull
public DataOrigin getDataOrigin() {
return mDataOrigin;
}
/**
* @return Record identifier if set, empty string otherwise
*/
@NonNull
public String getId() {
return mId;
}
/**
* Sets record identifier
*
* @hide
*/
public void setId(@NonNull String id) {
Objects.requireNonNull(id);
mId = id;
}
/** Returns recording method which indicates how data was recorded for the {@link Record} */
@RecordingMethod
public int getRecordingMethod() {
return mRecordingMethod;
}
/**
* @return Record's last modified time if set, Instant.EPOCH otherwise
*/
@NonNull
public Instant getLastModifiedTime() {
return mLastModifiedTime;
}
/**
* @return The device details that contributed to this record
*/
@NonNull
public Device getDevice() {
return mDevice;
}
/**
* Indicates whether some other object is "equal to" this one.
*
* @param object the reference object with which to compare.
* @return {@code true} if this object is the same as the obj
*/
@Override
public boolean equals(@NonNull Object object) {
if (this == object) return true;
if (object instanceof Metadata) {
Metadata other = (Metadata) object;
return getDevice().equals(other.getDevice())
&& getDataOrigin().equals(other.getDataOrigin())
&& getId().equals(other.getId())
&& Objects.equals(getClientRecordId(), other.getClientRecordId())
&& getClientRecordVersion() == other.getClientRecordVersion()
&& getRecordingMethod() == other.getRecordingMethod();
}
return false;
}
/**
* Returns a hash code value for the object.
*
* @return a hash code value for this object.
*/
@Override
public int hashCode() {
return Objects.hash(
getDevice(),
getDataOrigin(),
getId(),
getClientRecordId(),
getClientRecordVersion(),
getLastModifiedTime(),
getRecordingMethod());
}
/**
* List of possible Recording method for the {@link Record}.
*
* @hide
*/
@IntDef({
RECORDING_METHOD_UNKNOWN,
RECORDING_METHOD_ACTIVELY_RECORDED,
RECORDING_METHOD_AUTOMATICALLY_RECORDED,
RECORDING_METHOD_MANUAL_ENTRY
})
@Retention(RetentionPolicy.SOURCE)
public @interface RecordingMethod {}
/**
* @see Metadata
*/
public static final class Builder {
private Device mDevice = new Device.Builder().build();
private DataOrigin mDataOrigin = new DataOrigin.Builder().build();
private String mId = "";
private Instant mLastModifiedTime = Instant.EPOCH;
private String mClientRecordId;
private long mClientRecordVersion = 0;
@RecordingMethod private int mRecordingMethod = RECORDING_METHOD_UNKNOWN;
public Builder() {}
/** Sets optional client supplied device information associated with the data. */
@NonNull
public Builder setDevice(@NonNull Device device) {
Objects.requireNonNull(device);
mDevice = device;
return this;
}
/**
* Sets where the data comes from, such as application information originally generated this
* data. When {@link Record} is created before insertion, this contains a sentinel value,
* any assigned value will be ignored. After insertion, this will be populated with inserted
* application.
*/
@NonNull
public Builder setDataOrigin(@NonNull DataOrigin dataOrigin) {
Objects.requireNonNull(dataOrigin);
mDataOrigin = dataOrigin;
return this;
}
/**
* Sets unique identifier of this data, assigned by the Android Health Platform at insertion
* time. When {@link Record} is created before insertion, this takes a sentinel value, any
* assigned value will be ignored.
*/
@NonNull
public Builder setId(@NonNull String id) {
Objects.requireNonNull(id);
mId = id;
return this;
}
/**
* Sets when data was last modified (or originally created). When {@link Record} is created
* before inserted, this contains a sentinel value, any assigned value will be ignored.
*/
@NonNull
public Builder setLastModifiedTime(@NonNull Instant lastModifiedTime) {
Objects.requireNonNull(lastModifiedTime);
mLastModifiedTime = lastModifiedTime;
return this;
}
/**
* Sets optional client supplied record unique data identifier associated with the data.
* There is guaranteed a single entry for any type of data with same client provided
* identifier for a given client. Any new insertions with the same client provided
* identifier will either replace or be ignored depending on associated {@code
* clientRecordVersion}. @see clientRecordVersion
*
* <p>A null value means that no clientRecordId is set
*/
@NonNull
public Builder setClientRecordId(@Nullable String clientRecordId) {
mClientRecordId = clientRecordId;
return this;
}
/**
* Sets optional client supplied version associated with the data. This determines conflict
* resolution outcome when there are multiple insertions of the same {@code clientRecordId}.
* Data with the highest {@code clientRecordVersion} takes precedence. {@code
* clientRecordVersion} starts with 0. @see clientRecordId
*/
@NonNull
public Builder setClientRecordVersion(long clientRecordVersion) {
mClientRecordVersion = clientRecordVersion;
return this;
}
/**
* Sets recording method for the {@link Record}. This detail helps to know how the data was
* recorded which can be useful for prioritization of the record
*/
@NonNull
public Builder setRecordingMethod(@RecordingMethod int recordingMethod) {
mRecordingMethod = recordingMethod;
return this;
}
/**
* @return {@link Metadata} object
*/
@NonNull
public Metadata build() {
return new Metadata(
mDevice,
mDataOrigin,
mId,
mLastModifiedTime,
mClientRecordId,
mClientRecordVersion,
mRecordingMethod);
}
}
}