mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 843889 - Factor tabs code that is not Sync-specific out of org.mozilla.gecko.sync. r=rnewman
This commit is contained in:
parent
9972ba828c
commit
7f66dd3845
@ -34,6 +34,8 @@ SYNC_JAVA_FILES := \
|
||||
background/common/log/writers/StringLogWriter.java \
|
||||
background/common/log/writers/TagLogWriter.java \
|
||||
background/common/log/writers/ThreadLocalTagLogWriter.java \
|
||||
background/db/CursorDumper.java \
|
||||
background/db/Tab.java \
|
||||
sync/AlreadySyncingException.java \
|
||||
sync/CollectionKeys.java \
|
||||
sync/CommandProcessor.java \
|
||||
@ -249,7 +251,6 @@ SYNC_JAVA_FILES := \
|
||||
sync/stage/ServerSyncStage.java \
|
||||
sync/stage/SyncClientsEngineStage.java \
|
||||
sync/stage/UploadMetaGlobalStage.java \
|
||||
sync/StubActivity.java \
|
||||
sync/syncadapter/SyncAdapter.java \
|
||||
sync/syncadapter/SyncService.java \
|
||||
sync/SyncConfiguration.java \
|
||||
|
99
mobile/android/base/background/db/CursorDumper.java
Normal file
99
mobile/android/base/background/db/CursorDumper.java
Normal file
@ -0,0 +1,99 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.background.db;
|
||||
|
||||
import android.database.Cursor;
|
||||
|
||||
/**
|
||||
* A utility for dumping a cursor the debug log.
|
||||
* <p>
|
||||
* <b>For debugging only!</p>
|
||||
*/
|
||||
public class CursorDumper {
|
||||
protected static String fixedWidth(int width, String s) {
|
||||
if (s == null) {
|
||||
return spaces(width);
|
||||
}
|
||||
int length = s.length();
|
||||
if (width == length) {
|
||||
return s;
|
||||
}
|
||||
if (width > length) {
|
||||
return s + spaces(width - length);
|
||||
}
|
||||
return s.substring(0, width);
|
||||
}
|
||||
|
||||
protected static String spaces(int i) {
|
||||
return " ".substring(0, i);
|
||||
}
|
||||
|
||||
protected static String dashes(int i) {
|
||||
return "-------------------------------------".substring(0, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump a cursor to the debug log, ignoring any log level settings.
|
||||
* <p>
|
||||
* The position in the cursor is maintained. Caller is responsible for opening
|
||||
* and closing cursor.
|
||||
*
|
||||
* @param cursor
|
||||
* to dump.
|
||||
*/
|
||||
public static void dumpCursor(Cursor cursor) {
|
||||
dumpCursor(cursor, 18, "records");
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump a cursor to the debug log, ignoring any log level settings.
|
||||
* <p>
|
||||
* The position in the cursor is maintained. Caller is responsible for opening
|
||||
* and closing cursor.
|
||||
*
|
||||
* @param cursor
|
||||
* to dump.
|
||||
* @param columnWidth
|
||||
* how many characters per cursor column.
|
||||
* @param tags
|
||||
* a descriptor, printed like "(10 tags)", in the header row.
|
||||
*/
|
||||
protected static void dumpCursor(Cursor cursor, int columnWidth, String tags) {
|
||||
int originalPosition = cursor.getPosition();
|
||||
try {
|
||||
String[] columnNames = cursor.getColumnNames();
|
||||
int columnCount = cursor.getColumnCount();
|
||||
|
||||
for (int i = 0; i < columnCount; ++i) {
|
||||
System.out.print(fixedWidth(columnWidth, columnNames[i]) + " | ");
|
||||
}
|
||||
System.out.println("(" + cursor.getCount() + " " + tags + ")");
|
||||
for (int i = 0; i < columnCount; ++i) {
|
||||
System.out.print(dashes(columnWidth) + " | ");
|
||||
}
|
||||
System.out.println("");
|
||||
if (!cursor.moveToFirst()) {
|
||||
System.out.println("EMPTY");
|
||||
return;
|
||||
}
|
||||
|
||||
cursor.moveToFirst();
|
||||
while (!cursor.isAfterLast()) {
|
||||
for (int i = 0; i < columnCount; ++i) {
|
||||
System.out.print(fixedWidth(columnWidth, cursor.getString(i)) + " | ");
|
||||
}
|
||||
System.out.println("");
|
||||
cursor.moveToNext();
|
||||
}
|
||||
for (int i = 0; i < columnCount-1; ++i) {
|
||||
System.out.print(dashes(columnWidth + 3));
|
||||
}
|
||||
System.out.print(dashes(columnWidth + 3 - 1));
|
||||
System.out.println("");
|
||||
} finally {
|
||||
cursor.moveToPosition(originalPosition);
|
||||
}
|
||||
}
|
||||
}
|
81
mobile/android/base/background/db/Tab.java
Normal file
81
mobile/android/base/background/db/Tab.java
Normal file
@ -0,0 +1,81 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.background.db;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.mozilla.gecko.db.BrowserContract;
|
||||
import org.mozilla.gecko.db.BrowserContract.Tabs;
|
||||
import org.mozilla.gecko.sync.Utils;
|
||||
import org.mozilla.gecko.sync.repositories.android.RepoUtils;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
|
||||
// Immutable.
|
||||
public class Tab {
|
||||
public final String title;
|
||||
public final String icon;
|
||||
public final JSONArray history;
|
||||
public final long lastUsed;
|
||||
|
||||
public Tab(String title, String icon, JSONArray history, long lastUsed) {
|
||||
this.title = title;
|
||||
this.icon = icon;
|
||||
this.history = history;
|
||||
this.lastUsed = lastUsed;
|
||||
}
|
||||
|
||||
public ContentValues toContentValues(String clientGUID, int position) {
|
||||
ContentValues out = new ContentValues();
|
||||
out.put(BrowserContract.Tabs.POSITION, position);
|
||||
out.put(BrowserContract.Tabs.CLIENT_GUID, clientGUID);
|
||||
|
||||
out.put(BrowserContract.Tabs.FAVICON, this.icon);
|
||||
out.put(BrowserContract.Tabs.LAST_USED, this.lastUsed);
|
||||
out.put(BrowserContract.Tabs.TITLE, this.title);
|
||||
out.put(BrowserContract.Tabs.URL, (String) this.history.get(0));
|
||||
out.put(BrowserContract.Tabs.HISTORY, this.history.toJSONString());
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Tab)) {
|
||||
return false;
|
||||
}
|
||||
final Tab other = (Tab) o;
|
||||
|
||||
if (!RepoUtils.stringsEqual(this.title, other.title)) {
|
||||
return false;
|
||||
}
|
||||
if (!RepoUtils.stringsEqual(this.icon, other.icon)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(this.lastUsed == other.lastUsed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Utils.sameArrays(this.history, other.history);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a <code>Tab</code> from a cursor row.
|
||||
* <p>
|
||||
* Caller is responsible for creating, positioning, and closing the cursor.
|
||||
*
|
||||
* @param cursor
|
||||
* to inspect.
|
||||
* @return <code>Tab</code> instance.
|
||||
*/
|
||||
public static Tab fromCursor(final Cursor cursor) {
|
||||
final String title = RepoUtils.getStringFromCursor(cursor, Tabs.TITLE);
|
||||
final String icon = RepoUtils.getStringFromCursor(cursor, Tabs.FAVICON);
|
||||
final JSONArray history = RepoUtils.getJSONArrayFromCursor(cursor, Tabs.HISTORY);
|
||||
final long lastUsed = RepoUtils.getLongFromCursor(cursor, Tabs.LAST_USED);
|
||||
|
||||
return new Tab(title, icon, history, lastUsed);
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.sync;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
/*
|
||||
* Activity is just here for testing.
|
||||
*/
|
||||
public class StubActivity extends Activity {
|
||||
/** Called when the activity is first created. */
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ package org.mozilla.gecko.sync.repositories.android;
|
||||
import java.util.List;
|
||||
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.background.db.CursorDumper;
|
||||
import org.mozilla.gecko.db.BrowserContract;
|
||||
import org.mozilla.gecko.sync.repositories.NullCursorException;
|
||||
import org.mozilla.gecko.sync.repositories.domain.Record;
|
||||
@ -47,7 +48,7 @@ public abstract class AndroidBrowserRepositoryDataAccessor {
|
||||
Cursor cur = null;
|
||||
try {
|
||||
cur = queryHelper.safeQuery(".dumpDB", null, null, null, null);
|
||||
RepoUtils.dumpCursor(cur);
|
||||
CursorDumper.dumpCursor(cur);
|
||||
} catch (NullCursorException e) {
|
||||
} finally {
|
||||
if (cur != null) {
|
||||
|
@ -6,10 +6,9 @@ package org.mozilla.gecko.sync.repositories.android;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.background.db.Tab;
|
||||
import org.mozilla.gecko.db.BrowserContract;
|
||||
import org.mozilla.gecko.db.BrowserContract.Tabs;
|
||||
import org.mozilla.gecko.sync.repositories.InactiveSessionException;
|
||||
import org.mozilla.gecko.sync.repositories.NoContentProviderException;
|
||||
import org.mozilla.gecko.sync.repositories.NoStoreDelegateException;
|
||||
@ -22,7 +21,6 @@ import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionGuidsSince
|
||||
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionWipeDelegate;
|
||||
import org.mozilla.gecko.sync.repositories.domain.Record;
|
||||
import org.mozilla.gecko.sync.repositories.domain.TabsRecord;
|
||||
import org.mozilla.gecko.sync.repositories.domain.TabsRecord.Tab;
|
||||
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentValues;
|
||||
@ -293,24 +291,6 @@ public class FennecTabsRepository extends Repository {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a <code>Tab</code> from a cursor row.
|
||||
* <p>
|
||||
* Caller is responsible for creating, positioning, and closing the cursor.
|
||||
*
|
||||
* @param cursor
|
||||
* to inspect.
|
||||
* @return <code>Tab</code> instance.
|
||||
*/
|
||||
public static Tab tabFromCursor(final Cursor cursor) {
|
||||
final String title = RepoUtils.getStringFromCursor(cursor, Tabs.TITLE);
|
||||
final String icon = RepoUtils.getStringFromCursor(cursor, Tabs.FAVICON);
|
||||
final JSONArray history = RepoUtils.getJSONArrayFromCursor(cursor, Tabs.HISTORY);
|
||||
final long lastUsed = RepoUtils.getLongFromCursor(cursor, Tabs.LAST_USED);
|
||||
|
||||
return new Tab(title, icon, history, lastUsed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a <code>TabsRecord</code> from a cursor.
|
||||
* <p>
|
||||
@ -330,7 +310,7 @@ public class FennecTabsRepository extends Repository {
|
||||
public static TabsRecord tabsRecordFromCursor(final Cursor cursor, final String clientGuid, final String clientName) {
|
||||
final String collection = "tabs";
|
||||
final TabsRecord record = new TabsRecord(clientGuid, collection, 0, false);
|
||||
record.tabs = new ArrayList<TabsRecord.Tab>();
|
||||
record.tabs = new ArrayList<Tab>();
|
||||
record.clientName = clientName;
|
||||
|
||||
record.androidID = -1;
|
||||
@ -342,7 +322,7 @@ public class FennecTabsRepository extends Repository {
|
||||
try {
|
||||
cursor.moveToFirst();
|
||||
while (!cursor.isAfterLast()) {
|
||||
final Tab tab = FennecTabsRepository.tabFromCursor(cursor);
|
||||
final Tab tab = Tab.fromCursor(cursor);
|
||||
record.tabs.add(tab);
|
||||
|
||||
if (tab.lastUsed > record.lastModified) {
|
||||
|
@ -246,69 +246,6 @@ public class RepoUtils {
|
||||
return a.equals(b);
|
||||
}
|
||||
|
||||
private static String fixedWidth(int width, String s) {
|
||||
if (s == null) {
|
||||
return spaces(width);
|
||||
}
|
||||
int length = s.length();
|
||||
if (width == length) {
|
||||
return s;
|
||||
}
|
||||
if (width > length) {
|
||||
return s + spaces(width - length);
|
||||
}
|
||||
return s.substring(0, width);
|
||||
}
|
||||
|
||||
private static String spaces(int i) {
|
||||
return " ".substring(0, i);
|
||||
}
|
||||
|
||||
private static String dashes(int i) {
|
||||
return "-------------------------------------".substring(0, i);
|
||||
}
|
||||
|
||||
public static void dumpCursor(Cursor cur) {
|
||||
dumpCursor(cur, 18, "records");
|
||||
}
|
||||
|
||||
public static void dumpCursor(Cursor cur, int columnWidth, String tag) {
|
||||
int originalPosition = cur.getPosition();
|
||||
try {
|
||||
String[] columnNames = cur.getColumnNames();
|
||||
int columnCount = cur.getColumnCount();
|
||||
|
||||
for (int i = 0; i < columnCount; ++i) {
|
||||
System.out.print(fixedWidth(columnWidth, columnNames[i]) + " | ");
|
||||
}
|
||||
System.out.println("(" + cur.getCount() + " " + tag + ")");
|
||||
for (int i = 0; i < columnCount; ++i) {
|
||||
System.out.print(dashes(columnWidth) + " | ");
|
||||
}
|
||||
System.out.println("");
|
||||
if (!cur.moveToFirst()) {
|
||||
System.out.println("EMPTY");
|
||||
return;
|
||||
}
|
||||
|
||||
cur.moveToFirst();
|
||||
while (!cur.isAfterLast()) {
|
||||
for (int i = 0; i < columnCount; ++i) {
|
||||
System.out.print(fixedWidth(columnWidth, cur.getString(i)) + " | ");
|
||||
}
|
||||
System.out.println("");
|
||||
cur.moveToNext();
|
||||
}
|
||||
for (int i = 0; i < columnCount-1; ++i) {
|
||||
System.out.print(dashes(columnWidth + 3));
|
||||
}
|
||||
System.out.print(dashes(columnWidth + 3 - 1));
|
||||
System.out.println("");
|
||||
} finally {
|
||||
cur.moveToPosition(originalPosition);
|
||||
}
|
||||
}
|
||||
|
||||
public static String computeSQLInClause(int items, String field) {
|
||||
StringBuilder builder = new StringBuilder(field);
|
||||
builder.append(" IN (");
|
||||
|
@ -9,11 +9,11 @@ import java.util.ArrayList;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.background.db.Tab;
|
||||
import org.mozilla.gecko.db.BrowserContract;
|
||||
import org.mozilla.gecko.sync.ExtendedJSONObject;
|
||||
import org.mozilla.gecko.sync.NonArrayJSONException;
|
||||
import org.mozilla.gecko.sync.Utils;
|
||||
import org.mozilla.gecko.sync.repositories.android.RepoUtils;
|
||||
|
||||
import android.content.ContentValues;
|
||||
|
||||
@ -24,88 +24,7 @@ import android.content.ContentValues;
|
||||
*
|
||||
*/
|
||||
public class TabsRecord extends Record {
|
||||
|
||||
// Immutable.
|
||||
public static class Tab {
|
||||
public final String title;
|
||||
public final String icon;
|
||||
public final JSONArray history;
|
||||
public final long lastUsed;
|
||||
|
||||
public Tab(String title, String icon, JSONArray history, long lastUsed) {
|
||||
this.title = title;
|
||||
this.icon = icon;
|
||||
this.history = history;
|
||||
this.lastUsed = lastUsed;
|
||||
}
|
||||
|
||||
public static Tab fromJSONObject(JSONObject o) throws NonArrayJSONException {
|
||||
ExtendedJSONObject obj = new ExtendedJSONObject(o);
|
||||
String title = obj.getString("title");
|
||||
String icon = obj.getString("icon");
|
||||
JSONArray history = obj.getArray("urlHistory");
|
||||
|
||||
// Last used is inexplicably a string in seconds. Most of the time.
|
||||
long lastUsed = 0;
|
||||
Object lU = obj.get("lastUsed");
|
||||
if (lU instanceof Number) {
|
||||
lastUsed = ((Long) lU) * 1000L;
|
||||
} else if (lU instanceof String) {
|
||||
try {
|
||||
lastUsed = Long.parseLong((String) lU, 10) * 1000L;
|
||||
} catch (NumberFormatException e) {
|
||||
Logger.debug(LOG_TAG, "Invalid number format in lastUsed: " + lU);
|
||||
}
|
||||
}
|
||||
return new Tab(title, icon, history, lastUsed);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public JSONObject toJSONObject() {
|
||||
JSONObject o = new JSONObject();
|
||||
o.put("title", title);
|
||||
o.put("icon", icon);
|
||||
o.put("urlHistory", history);
|
||||
o.put("lastUsed", this.lastUsed / 1000);
|
||||
return o;
|
||||
}
|
||||
|
||||
public ContentValues toContentValues(String clientGUID, int position) {
|
||||
ContentValues out = new ContentValues();
|
||||
out.put(BrowserContract.Tabs.POSITION, position);
|
||||
out.put(BrowserContract.Tabs.CLIENT_GUID, clientGUID);
|
||||
|
||||
out.put(BrowserContract.Tabs.FAVICON, this.icon);
|
||||
out.put(BrowserContract.Tabs.LAST_USED, this.lastUsed);
|
||||
out.put(BrowserContract.Tabs.TITLE, this.title);
|
||||
out.put(BrowserContract.Tabs.URL, (String) this.history.get(0));
|
||||
out.put(BrowserContract.Tabs.HISTORY, this.history.toJSONString());
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Tab)) {
|
||||
return false;
|
||||
}
|
||||
final Tab other = (Tab) o;
|
||||
|
||||
if (!RepoUtils.stringsEqual(this.title, other.title)) {
|
||||
return false;
|
||||
}
|
||||
if (!RepoUtils.stringsEqual(this.icon, other.icon)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(this.lastUsed == other.lastUsed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Utils.sameArrays(this.history, other.history);
|
||||
}
|
||||
}
|
||||
|
||||
private static final String LOG_TAG = "TabsRecord";
|
||||
public static final String LOG_TAG = "TabsRecord";
|
||||
|
||||
public static final String COLLECTION_NAME = "tabs";
|
||||
public static final long TABS_TTL = 7 * 24 * 60 * 60; // 7 days in seconds.
|
||||
@ -145,7 +64,7 @@ public class TabsRecord extends Record {
|
||||
protected static JSONArray tabsToJSON(ArrayList<Tab> tabs) {
|
||||
JSONArray out = new JSONArray();
|
||||
for (Tab tab : tabs) {
|
||||
out.add(tab.toJSONObject());
|
||||
out.add(tabToJSONObject(tab));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@ -155,7 +74,7 @@ public class TabsRecord extends Record {
|
||||
for (Object o : in) {
|
||||
if (o instanceof JSONObject) {
|
||||
try {
|
||||
tabs.add(Tab.fromJSONObject((JSONObject) o));
|
||||
tabs.add(TabsRecord.tabFromJSONObject((JSONObject) o));
|
||||
} catch (NonArrayJSONException e) {
|
||||
Logger.warn(LOG_TAG, "urlHistory is not an array for this tab.", e);
|
||||
}
|
||||
@ -200,4 +119,35 @@ public class TabsRecord extends Record {
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public static Tab tabFromJSONObject(JSONObject o) throws NonArrayJSONException {
|
||||
ExtendedJSONObject obj = new ExtendedJSONObject(o);
|
||||
String title = obj.getString("title");
|
||||
String icon = obj.getString("icon");
|
||||
JSONArray history = obj.getArray("urlHistory");
|
||||
|
||||
// Last used is inexplicably a string in seconds. Most of the time.
|
||||
long lastUsed = 0;
|
||||
Object lU = obj.get("lastUsed");
|
||||
if (lU instanceof Number) {
|
||||
lastUsed = ((Long) lU) * 1000L;
|
||||
} else if (lU instanceof String) {
|
||||
try {
|
||||
lastUsed = Long.parseLong((String) lU, 10) * 1000L;
|
||||
} catch (NumberFormatException e) {
|
||||
Logger.debug(TabsRecord.LOG_TAG, "Invalid number format in lastUsed: " + lU);
|
||||
}
|
||||
}
|
||||
return new Tab(title, icon, history, lastUsed);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static JSONObject tabToJSONObject(Tab tab) {
|
||||
JSONObject o = new JSONObject();
|
||||
o.put("title", tab.title);
|
||||
o.put("icon", tab.icon);
|
||||
o.put("urlHistory", tab.history);
|
||||
o.put("lastUsed", tab.lastUsed / 1000);
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,8 @@ background/common/log/writers/SimpleTagLogWriter.java
|
||||
background/common/log/writers/StringLogWriter.java
|
||||
background/common/log/writers/TagLogWriter.java
|
||||
background/common/log/writers/ThreadLocalTagLogWriter.java
|
||||
background/db/CursorDumper.java
|
||||
background/db/Tab.java
|
||||
sync/AlreadySyncingException.java
|
||||
sync/CollectionKeys.java
|
||||
sync/CommandProcessor.java
|
||||
@ -237,7 +239,6 @@ sync/stage/SafeConstrainedServer11Repository.java
|
||||
sync/stage/ServerSyncStage.java
|
||||
sync/stage/SyncClientsEngineStage.java
|
||||
sync/stage/UploadMetaGlobalStage.java
|
||||
sync/StubActivity.java
|
||||
sync/syncadapter/SyncAdapter.java
|
||||
sync/syncadapter/SyncService.java
|
||||
sync/SyncConfiguration.java
|
||||
|
Loading…
Reference in New Issue
Block a user