/*
* Copyright 2011 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.ipc.invalidation.external.client.android;
import com.google.common.base.Preconditions;
import android.accounts.Account;
import android.content.Context;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Factory for obtaining an {@code InvalidationClient} for the Android platform. The {@link #create}
* method will create a invalidation client associated with a particular application and user
* account.
* <p>
* Applications should persist the unique client key for the new client so invalidation activity can
* restart later if the application is removed from memory. An application can obtain an
* invalidation client instance to resume activity by calling the {@link #resume} method with the
* same application id that was originally passed to {@link #create}.
*
*/
public class AndroidClientFactory {
/**
* A mapping of application id to invalidation client instances that can be used to
* resume/reassociate an existing invalidation client. Client instances are not guaranteed (nor
* required) to be reused.
*/
private static final Map<String, AndroidInvalidationClientImpl> clientMap =
new ConcurrentHashMap<String, AndroidInvalidationClientImpl>();
/**
* Starts a new invalidation client for the provided application and account token that will
* deliver invalidation events to an instance of the provided listener component.
* <p>
* The implementation of this method is idempotent. If you call {@link #create} more than once
* with the same application id, account, and listenerName values, all calls after the first one
* are equivalent to just calling {@link #resume} with the same application id.
*
* @param context the context for the client.
* @param clientKey a unique id that identifies the created client within the scope of the
* application.
* @param account user account that is registering the invalidations.
* @param authType the authentication token type that should be used to authenticate the client.
* @param listenerClass the {@link AndroidInvalidationListener} subclass that is registered to
* receive the broadcast intents for invalidation events.
*/
public static AndroidInvalidationClient create(Context context, String clientKey, int clientType,
Account account, String authType,
Class<? extends AndroidInvalidationListener> listenerClass) {
Preconditions.checkNotNull(context, "context");
Preconditions.checkNotNull(account, "account");
Preconditions.checkArgument((authType != null) && (authType.length() != 0),
"authType must be a non-empty string value");
Preconditions.checkNotNull(listenerClass, "listenerClass");
AndroidInvalidationClientImpl client = null;
if (clientMap.containsKey(clientKey)) {
return resume(context, clientKey);
}
if (client == null) {
client = new AndroidInvalidationClientImpl(context, clientKey, clientType, account, authType,
listenerClass);
client.initialize();
clientMap.put(clientKey, client);
}
return client;
}
/**
* Creates a new AndroidInvalidationClient instance that is resuming processing for an existing
* application id.
* <p>
* Use of this method is not recommended: use {@link #create} instead.
*
* @param context the context for the client.
* @param clientKey a unique key that identifies the created client within the scope of the
* device.
*/
public static AndroidInvalidationClient resume(Context context, String clientKey) {
return resume(context, clientKey, true);
}
/**
* Creates a new AndroidInvalidationClient instance that is resuming processing for an existing
* application id.
* <p>
* Use of this method is not recommended: use {@link #create} instead.
*
* @param context the context for the client.
* @param clientKey a unique key that identifies the created client within the scope of the
* device.
* @param sendTiclResumeRequest whether to send a request to the service to resume the Ticl. If
* {@code false}, assumes the Ticl is already loaded at the service. This is used in the
* listener implementation and should not be set by clients.
*/
public static AndroidInvalidationClient resume(Context context, String clientKey,
boolean sendTiclResumeRequest) {
Preconditions.checkNotNull(context, "context");
// See if a cached entry is available with a matching application id
AndroidInvalidationClientImpl client = clientMap.get(clientKey);
if (client != null) {
// Notify the client instance that it has multiple references.
client.addReference();
} else {
// Attempt to resume the client using the invalidation service
client = new AndroidInvalidationClientImpl(context, clientKey);
client.initResumed(sendTiclResumeRequest);
}
return client;
}
/**
* Release the client instance associated with the provided key. Any transient resources
* associated with the client in the factory will be released.
*
* @param clientKey the client to remove
* @return {@code true} if a client with the provided key was found and releasedUUUU .
*/
static boolean release(String clientKey) {
return clientMap.remove(clientKey) != null;
}
/**
* Resets the state of the factory by dropping all cached client references.
*/
static void resetForTest() {
clientMap.clear();
}
}