Initial checkin of AOSP Messaging app.

b/23110861

Change-Id: I11db999bd10656801e618f78ab2b2ef74136fff1
This commit is contained in:
Mike Dodd
2015-08-12 12:47:26 -07:00
parent ef8c7abbcf
commit d3b009ae55
1645 changed files with 186271 additions and 0 deletions
@@ -0,0 +1,72 @@
/*
* Copyright (C) 2015 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 com.android.messaging.datamodel.binding;
/**
* Base class for data objects that will be bound to a piece of the UI
*/
public abstract class BindableData {
/**
* Called by Binding during unbind to allow data to proactively unregister callbacks
* Data instance should release all listeners that may call back to the host UI
*/
protected abstract void unregisterListeners();
/**
* Key used to identify the piece of UI that the data is currently bound to
*/
private String mBindingId;
/**
* Bind this data to the ui host - checks data is currently unbound
*/
public void bind(final String bindingId) {
if (isBound() || bindingId == null) {
throw new IllegalStateException();
}
mBindingId = bindingId;
}
/**
* Unbind this data from the ui host - checks that the data is currently bound to specified id
*/
public void unbind(final String bindingId) {
if (!isBound(bindingId)) {
throw new IllegalStateException();
}
unregisterListeners();
mBindingId = null;
}
/**
* Check to see if the data is bound to anything
*
* TODO: This should be package private because it's supposed to only be used by Binding,
* however, several classes call this directly. We want the classes to track what they are
* bound to.
*/
protected boolean isBound() {
return (mBindingId != null);
}
/**
* Check to see if data is still bound with specified bindingId before calling over to ui
*/
public boolean isBound(final String bindingId) {
return (bindingId.equals(mBindingId));
}
}
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2015 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 com.android.messaging.datamodel.binding;
/**
* A BindableData that's only used to be bound once. If the client needs to rebind, it needs
* to create a new instance of the BindableOnceData.
*/
public abstract class BindableOnceData extends BindableData {
private boolean boundOnce = false;
@Override
public void bind(final String bindingId) {
// Ensures that we can't re-bind again after the first binding.
if (boundOnce) {
throw new IllegalStateException();
}
super.bind(bindingId);
boundOnce = true;
}
/**
* Checks if the instance is bound to anything.
*/
@Override
public boolean isBound() {
return super.isBound();
}
}
@@ -0,0 +1,94 @@
/*
* Copyright (C) 2015 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 com.android.messaging.datamodel.binding;
import java.util.concurrent.atomic.AtomicLong;
public class Binding<T extends BindableData> extends BindingBase<T> {
private static AtomicLong sBindingIdx = new AtomicLong(System.currentTimeMillis() * 1000);
private String mBindingId;
private T mData;
private final Object mOwner;
private boolean mWasBound;
/**
* Initialize a binding instance - the owner is typically the containing class
*/
Binding(final Object owner) {
mOwner = owner;
}
@Override
public T getData() {
ensureBound();
return mData;
}
@Override
public boolean isBound() {
return (mData != null && mData.isBound(mBindingId));
}
@Override
public boolean isBound(final T data) {
return (isBound() && data == mData);
}
@Override
public void ensureBound() {
if (!isBound()) {
throw new IllegalStateException("not bound; wasBound = " + mWasBound);
}
}
@Override
public void ensureBound(final T data) {
if (!isBound()) {
throw new IllegalStateException("not bound; wasBound = " + mWasBound);
} else if (data != mData) {
throw new IllegalStateException("not bound to correct data " + data + " vs " + mData);
}
}
@Override
public String getBindingId() {
return mBindingId;
}
public void bind(final T data) {
// Check both this binding and the data not already bound
if (mData != null || data.isBound()) {
throw new IllegalStateException("already bound when binding to " + data);
}
// Generate a unique identifier for this bind call
mBindingId = Long.toHexString(sBindingIdx.getAndIncrement());
data.bind(mBindingId);
mData = data;
mWasBound = true;
}
public void unbind() {
// Check this binding is bound and that data is bound to this binding
if (mData == null || !mData.isBound(mBindingId)) {
throw new IllegalStateException("not bound when unbind");
}
mData.unbind(mBindingId);
mData = null;
mBindingId = null;
}
}
@@ -0,0 +1,84 @@
/*
* Copyright (C) 2015 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 com.android.messaging.datamodel.binding;
/**
* The binding class keeps track of a binding between a ui component and an item of BindableData
* It allows each side to ensure that when it communicates with the other they are still bound
* together.
* NOTE: Ensure that the UI component uses the same binding instance for it's whole lifetime
* (DO NOT CREATE A NEW BINDING EACH TIME A NEW PIECE OF DATA IS BOUND)...
*
* The ui component owns the binding instance.
* It can use it [isBound(data)] to see if the binding still binds to the right piece of data
*
* Upon binding the data is informed of a unique binding key generated in this class and can use
* that to ensure that it is still issuing callbacks to the right piece of ui.
*/
public abstract class BindingBase<T extends BindableData> {
/**
* Creates a new exclusively owned binding for the owner object.
*/
public static <T extends BindableData> Binding<T> createBinding(final Object owner) {
return new Binding<T>(owner);
}
/**
* Creates a new read-only binding referencing the source binding object.
* TODO: We may want to refcount the Binding references, so that when the binding owner
* calls unbind() when there's still outstanding references we can catch it.
*/
public static <T extends BindableData> ImmutableBindingRef<T> createBindingReference(
final BindingBase<T> srcBinding) {
return new ImmutableBindingRef<T>(srcBinding);
}
/**
* Creates a detachable binding for the owner object. Use this if your owner object is a UI
* component that may undergo a "detached from window" -> "re-attached to window" transition.
*/
public static <T extends BindableData> DetachableBinding<T> createDetachableBinding(
final Object owner) {
return new DetachableBinding<T>(owner);
}
public abstract T getData();
/**
* Check if binding connects to the specified data instance
*/
public abstract boolean isBound();
/**
* Check if binding connects to the specified data instance
*/
public abstract boolean isBound(final T data);
/**
* Throw if binding connects to the specified data instance
*/
public abstract void ensureBound();
/**
* Throw if binding connects to the specified data instance
*/
public abstract void ensureBound(final T data);
/**
* Return the binding id for this binding (will be null if not bound)
*/
public abstract String getBindingId();
}
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2015 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 com.android.messaging.datamodel.binding;
import com.android.messaging.util.Assert;
/**
* An extension on {@link Binding} that allows for temporary data detachment from the UI component.
* This is used when, instead of destruction or data rebinding, the owning UI undergoes a
* "detached from window" -> "re-attached to window" transition, in which case we want to
* temporarily unbind the data and remember it so that it can be rebound when the UI is re-attached
* to window later.
*/
public class DetachableBinding<T extends BindableData> extends Binding<T> {
private T mDetachedData;
DetachableBinding(Object owner) {
super(owner);
}
@Override
public void bind(T data) {
super.bind(data);
// Rebinding before re-attaching. Pre-emptively throw away the detached data because
// it's now stale.
mDetachedData = null;
}
public void detach() {
Assert.isNull(mDetachedData);
Assert.isTrue(isBound());
mDetachedData = getData();
unbind();
}
public void reAttachIfPossible() {
if (mDetachedData != null) {
Assert.isFalse(isBound());
bind(mDetachedData);
mDetachedData = null;
}
}
}
@@ -0,0 +1,83 @@
/*
* Copyright (C) 2015 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 com.android.messaging.datamodel.binding;
import com.android.messaging.util.Assert;
/**
* A immutable wrapper around a Binding object. Callers can only access readonly methods like
* getData(), isBound() and ensureBound() but not bind() and unbind(). This is used for MVC pattern
* where both the View and the Controller needs access to a centrally bound Model object. The View
* is the one that owns the bind/unbind logic of the Binding, whereas controller only serves as a
* consumer.
*/
public class ImmutableBindingRef<T extends BindableData> extends BindingBase<T> {
/**
* The referenced, read-only binding object.
*/
private final BindingBase<T> mBinding;
/**
* Hidden ctor.
*/
ImmutableBindingRef(final BindingBase<T> binding) {
mBinding = resolveBinding(binding);
}
@Override
public T getData() {
return mBinding.getData();
}
@Override
public boolean isBound() {
return mBinding.isBound();
}
@Override
public boolean isBound(final T data) {
return mBinding.isBound(data);
}
@Override
public void ensureBound() {
mBinding.ensureBound();
}
@Override
public void ensureBound(final T data) {
mBinding.ensureBound(data);
}
@Override
public String getBindingId() {
return mBinding.getBindingId();
}
/**
* Resolve the source binding to the real BindingImpl it's referencing. This avoids the
* redundancy of multiple wrapper calls when creating a binding reference from an existing
* binding reference.
*/
private BindingBase<T> resolveBinding(final BindingBase<T> binding) {
BindingBase<T> resolvedBinding = binding;
while (resolvedBinding instanceof ImmutableBindingRef<?>) {
resolvedBinding = ((ImmutableBindingRef<T>) resolvedBinding).mBinding;
}
Assert.isTrue(resolvedBinding instanceof Binding<?>);
return resolvedBinding;
}
}