Bug 1097220 - Extend client record format on Android. r=nalexander

This commit is contained in:
Richard Newman 2014-11-18 18:40:29 -08:00
Родитель 8454984605
Коммит 0aacd30a3a
9 изменённых файлов: 190 добавлений и 15 удалений

Просмотреть файл

@ -7,6 +7,7 @@ package org.mozilla.gecko.sync;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.GlobalConstants;
import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
import org.mozilla.gecko.util.HardwareUtils;
import android.content.Context;
import android.content.SharedPreferences;
@ -22,6 +23,9 @@ public class SharedPreferencesClientsDataDelegate implements ClientsDataDelegate
public SharedPreferencesClientsDataDelegate(SharedPreferences sharedPreferences, Context context) {
this.sharedPreferences = sharedPreferences;
this.context = context;
// It's safe to init this multiple times.
HardwareUtils.init(context);
}
@Override
@ -92,4 +96,21 @@ public class SharedPreferencesClientsDataDelegate implements ClientsDataDelegate
public long getLastModifiedTimestamp() {
return sharedPreferences.getLong(SyncConfiguration.PREF_CLIENT_DATA_TIMESTAMP, 0);
}
@Override
public String getFormFactor() {
if (HardwareUtils.isLargeTablet()) {
return "largetablet";
}
if (HardwareUtils.isSmallTablet()) {
return "smalltablet";
}
if (HardwareUtils.isTelevision()) {
return "tv";
}
return "phone";
}
}

Просмотреть файл

@ -12,6 +12,7 @@ public interface ClientsDataDelegate {
public void setClientsCount(int clientsCount);
public int getClientsCount();
public boolean isLocalGUID(String guid);
public String getFormFactor();
/**
* The last time the client's data was modified in a way that should be

Просмотреть файл

@ -19,7 +19,7 @@ public class ClientsDatabase extends CachedSQLiteOpenHelper {
// Database Specifications.
protected static final String DB_NAME = "clients_database";
protected static final int SCHEMA_VERSION = 2;
protected static final int SCHEMA_VERSION = 3;
// Clients Table.
public static final String TBL_CLIENTS = "clients";
@ -28,7 +28,15 @@ public class ClientsDatabase extends CachedSQLiteOpenHelper {
public static final String COL_NAME = "name";
public static final String COL_TYPE = "device_type";
public static final String[] TBL_CLIENTS_COLUMNS = new String[] { COL_ACCOUNT_GUID, COL_PROFILE, COL_NAME, COL_TYPE };
// Optional fields.
public static final String COL_FORMFACTOR = "formfactor";
public static final String COL_OS = "os";
public static final String COL_APPLICATION = "application";
public static final String COL_APP_PACKAGE = "appPackage";
public static final String COL_DEVICE = "device";
public static final String[] TBL_CLIENTS_COLUMNS = new String[] { COL_ACCOUNT_GUID, COL_PROFILE, COL_NAME, COL_TYPE,
COL_FORMFACTOR, COL_OS, COL_APPLICATION, COL_APP_PACKAGE, COL_DEVICE };
public static final String TBL_CLIENTS_KEY = COL_ACCOUNT_GUID + " = ? AND " +
COL_PROFILE + " = ?";
@ -65,6 +73,11 @@ public class ClientsDatabase extends CachedSQLiteOpenHelper {
+ COL_PROFILE + " TEXT, "
+ COL_NAME + " TEXT, "
+ COL_TYPE + " TEXT, "
+ COL_FORMFACTOR + " TEXT, "
+ COL_OS + " TEXT, "
+ COL_APPLICATION + " TEXT, "
+ COL_APP_PACKAGE + " TEXT, "
+ COL_DEVICE + " TEXT, "
+ "PRIMARY KEY (" + COL_ACCOUNT_GUID + ", " + COL_PROFILE + "))";
db.execSQL(createClientsTableSql);
}
@ -82,16 +95,28 @@ public class ClientsDatabase extends CachedSQLiteOpenHelper {
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Logger.debug(LOG_TAG, "ClientsDatabase.onUpgrade().");
// For now we'll just drop and recreate the tables.
db.execSQL("DROP TABLE IF EXISTS " + TBL_CLIENTS);
db.execSQL("DROP TABLE IF EXISTS " + TBL_COMMANDS);
onCreate(db);
Logger.debug(LOG_TAG, "ClientsDatabase.onUpgrade(" + oldVersion + ", " + newVersion + ").");
if (oldVersion < 2) {
// For now we'll just drop and recreate the tables.
db.execSQL("DROP TABLE IF EXISTS " + TBL_CLIENTS);
db.execSQL("DROP TABLE IF EXISTS " + TBL_COMMANDS);
onCreate(db);
return;
}
if (newVersion >= 3) {
// Add the optional columns to clients.
db.execSQL("ALTER TABLE " + TBL_CLIENTS + " ADD COLUMN " + COL_FORMFACTOR + " TEXT");
db.execSQL("ALTER TABLE " + TBL_CLIENTS + " ADD COLUMN " + COL_OS + " TEXT");
db.execSQL("ALTER TABLE " + TBL_CLIENTS + " ADD COLUMN " + COL_APPLICATION + " TEXT");
db.execSQL("ALTER TABLE " + TBL_CLIENTS + " ADD COLUMN " + COL_APP_PACKAGE + " TEXT");
db.execSQL("ALTER TABLE " + TBL_CLIENTS + " ADD COLUMN " + COL_DEVICE + " TEXT");
}
}
public void wipeDB() {
SQLiteDatabase db = this.getCachedWritableDatabase();
onUpgrade(db, SCHEMA_VERSION, SCHEMA_VERSION);
onUpgrade(db, 0, SCHEMA_VERSION);
}
public void wipeClientsTable() {
@ -115,6 +140,26 @@ public class ClientsDatabase extends CachedSQLiteOpenHelper {
cv.put(COL_NAME, record.name);
cv.put(COL_TYPE, record.type);
if (record.formfactor != null) {
cv.put(COL_FORMFACTOR, record.formfactor);
}
if (record.os != null) {
cv.put(COL_OS, record.os);
}
if (record.application != null) {
cv.put(COL_APPLICATION, record.application);
}
if (record.appPackage != null) {
cv.put(COL_APP_PACKAGE, record.appPackage);
}
if (record.device != null) {
cv.put(COL_DEVICE, record.device);
}
String[] args = new String[] { record.guid, profileId };
int rowsUpdated = db.update(TBL_CLIENTS, cv, TBL_CLIENTS_KEY, args);

Просмотреть файл

@ -118,12 +118,21 @@ public class ClientsDatabaseAccessor {
}
protected static ClientRecord recordFromCursor(Cursor cur) {
String accountGUID = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_ACCOUNT_GUID);
String clientName = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_NAME);
String clientType = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_TYPE);
ClientRecord record = new ClientRecord(accountGUID);
final String accountGUID = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_ACCOUNT_GUID);
final String clientName = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_NAME);
final String clientType = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_TYPE);
final ClientRecord record = new ClientRecord(accountGUID);
record.name = clientName;
record.type = clientType;
// Optional fields. These will either be null or strings.
record.formfactor = RepoUtils.optStringFromCursor(cur, ClientsDatabase.COL_FORMFACTOR);
record.os = RepoUtils.optStringFromCursor(cur, ClientsDatabase.COL_OS);
record.device = RepoUtils.optStringFromCursor(cur, ClientsDatabase.COL_DEVICE);
record.appPackage = RepoUtils.optStringFromCursor(cur, ClientsDatabase.COL_APP_PACKAGE);
record.application = RepoUtils.optStringFromCursor(cur, ClientsDatabase.COL_APPLICATION);
return record;
}

Просмотреть файл

@ -95,7 +95,24 @@ public class RepoUtils {
}
}
public static String getStringFromCursor(Cursor cur, String colId) {
/**
* This method exists because the behavior of <code>cur.getString()</code> is undefined
* when the value in the database is <code>NULL</code>.
* This method will return <code>null</code> in that case.
*/
public static String optStringFromCursor(final Cursor cur, final String colId) {
final int col = cur.getColumnIndex(colId);
if (cur.isNull(col)) {
return null;
}
return cur.getString(col);
}
/**
* The behavior of this method when the value in the database is <code>NULL</code> is
* determined by the implementation of the {@link Cursor}.
*/
public static String getStringFromCursor(final Cursor cur, final String colId) {
// TODO: getColumnIndexOrThrow?
// TODO: don't look up columns by name!
return cur.getString(cur.getColumnIndex(colId));

Просмотреть файл

@ -41,6 +41,16 @@ public class ClientRecord extends Record {
public JSONArray commands;
public JSONArray protocols;
// Optional fields.
// See <https://github.com/mozilla-services/docs/blob/master/source/sync/objectformats.rst#user-content-clients>
// for full formats.
// If a value isn't known, the field is omitted.
public String formfactor; // "phone", "largetablet", "smalltablet", "desktop", "laptop", "tv".
public String os; // One of "Android", "Darwin", "WINNT", "Linux", "iOS", "Firefox OS".
public String application; // Display name, E.g., "Firefox Beta"
public String appPackage; // E.g., "org.mozilla.firefox_beta"
public String device; // E.g., "HTC One"
public ClientRecord(String guid, String collection, long lastModified, boolean deleted) {
super(guid, collection, lastModified, deleted);
this.ttl = CLIENTS_TTL;
@ -85,6 +95,26 @@ public class ClientRecord extends Record {
Logger.debug(LOG_TAG, "Got non-array protocols in client record " + guid, e);
protocols = null;
}
if (payload.containsKey("formfactor")) {
this.formfactor = payload.getString("formfactor");
}
if (payload.containsKey("os")) {
this.os = payload.getString("os");
}
if (payload.containsKey("application")) {
this.application = payload.getString("application");
}
if (payload.containsKey("appPackage")) {
this.appPackage = payload.getString("appPackage");
}
if (payload.containsKey("device")) {
this.device = payload.getString("device");
}
}
@Override
@ -101,6 +131,27 @@ public class ClientRecord extends Record {
if (this.protocols != null) {
payload.put("protocols", this.protocols);
}
if (this.formfactor != null) {
payload.put("formfactor", this.formfactor);
}
if (this.os != null) {
payload.put("os", this.os);
}
if (this.application != null) {
payload.put("application", this.application);
}
if (this.appPackage != null) {
payload.put("appPackage", this.appPackage);
}
if (this.device != null) {
payload.put("device", this.device);
}
}
@Override
@ -123,7 +174,7 @@ public class ClientRecord extends Record {
return false;
}
// Don't compare versions or protocols, no matter how much we might want to.
// Don't compare versions, protocols, or other optional fields, no matter how much we might want to.
// They're not required by the spec.
ClientRecord other = (ClientRecord) o;
if (!RepoUtils.stringsEqual(other.name, this.name) ||
@ -144,6 +195,13 @@ public class ClientRecord extends Record {
out.type = this.type;
out.version = this.version;
out.protocols = this.protocols;
out.formfactor = this.formfactor;
out.os = this.os;
out.application = this.application;
out.appPackage = this.appPackage;
out.device = this.device;
return out;
}

Просмотреть файл

@ -13,6 +13,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.background.common.GlobalConstants;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.sync.CommandProcessor;
@ -38,7 +39,9 @@ import org.mozilla.gecko.sync.repositories.android.RepoUtils;
import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
import org.mozilla.gecko.sync.repositories.domain.ClientRecordFactory;
import org.mozilla.gecko.sync.repositories.domain.VersionConstants;
import org.mozilla.gecko.util.HardwareUtils;
import android.content.Context;
import ch.boye.httpclientandroidlib.HttpStatus;
public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage {
@ -378,6 +381,13 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage {
r.name = ourName;
r.version = getLocalClientVersion();
r.protocols = getLocalClientProtocols();
r.os = "Android";
r.application = GlobalConstants.MOZ_APP_DISPLAYNAME;
r.appPackage = AppConstants.ANDROID_PACKAGE_NAME;
r.device = android.os.Build.MODEL;
r.formfactor = delegate.getFormFactor();
return r;
}

Просмотреть файл

@ -63,16 +63,25 @@ public class TestClientsStage extends AndroidSyncTestCase {
}
};
String guid = "clientabcdef";
final String guid = "clientabcdef";
long lastModified = System.currentTimeMillis();
ClientRecord record = new ClientRecord(guid, "clients", lastModified , false);
record.name = "John's Phone";
record.type = "mobile";
record.device = "Some Device";
record.os = "iOS";
record.commands = new JSONArray();
dataAccessor.store(record);
assertEquals(1, dataAccessor.clientsCount());
final ClientRecord stored = dataAccessor.fetchAllClients().get(guid);
assertNotNull(stored);
assertEquals("John's Phone", stored.name);
assertEquals("mobile", stored.type);
assertEquals("Some Device", stored.device);
assertEquals("iOS", stored.os);
stage.wipeLocal(session);
try {

Просмотреть файл

@ -58,4 +58,9 @@ public class MockClientsDataDelegate implements ClientsDataDelegate {
public synchronized long getLastModifiedTimestamp() {
return clientDataTimestamp;
}
@Override
public String getFormFactor() {
return "phone";
}
}