Use a SyncProvider to sync all Campaigns

The SyncService constantly deletes and re-creates the campaigns
in the background to make sure they are up to date

FIXME: Handle deleted or renamed campaigns

Change-Id: I5d03995ada219481ea38887a8ea6d59fa11d2ac8
This commit is contained in:
YuviPanda 2013-09-06 05:54:13 +05:30
parent a56fa072a1
commit 2503f54731
13 changed files with 551 additions and 136 deletions

View file

@ -105,6 +105,17 @@
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
<service
android:name=".campaigns.CampaignsSyncService"
android:exported="true">
<intent-filter>
<action
android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/campaigns_sync_adapter" />
</service>
<service
android:name=".contributions.ContributionsSyncService"
android:exported="true">
@ -136,6 +147,13 @@
android:authorities="org.wikimedia.commons.contributions.contentprovider"
android:exported="false">
</provider>
<provider
android:name=".campaigns.CampaignsContentProvider"
android:label="@string/provider_campaigns"
android:syncable="true"
android:authorities="org.wikimedia.commons.campaigns.contentprovider"
android:exported="false">
</provider>
<provider
android:name=".modifications.ModificationsContentProvider"
android:label="@string/provider_modifications"

View file

@ -5,9 +5,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/campaignsListFragment"
class="org.wikimedia.commons.campaigns.CampaignsListFragment"
<ListView
android:id="@+id/campaignsList"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

View file

@ -102,4 +102,5 @@
<string name="welcome_copyright_subtext">Avoid copyrighted materials you found from the Internet as well as images of posters, book covers, etc.</string>
<string name="welcome_final_text">You think you got it?</string>
<string name="welcome_final_button_text">Yes!</string>
<string name="provider_campaigns">Campaigns</string>
</resources>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="org.wikimedia.commons.campaigns.contentprovider"
android:accountType="org.wikimedia.commons"
android:supportsUploading="false"
android:userVisible="true"
android:isAlwaysSyncable="true"
/>

View file

@ -1,6 +1,10 @@
package org.wikimedia.commons.campaigns;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
@ -17,16 +21,27 @@ public class Campaign {
private String defaultDescription;
private JSONObject config;
private String body;
private boolean isParsed;
private String trackingCategory;
private String description;
private String title;
public boolean isEnabled() {
return enabled;
}
public String getAutoAddWikitext() {
if(!this.isParsed) {
this.parseConfig();
}
return autoAddWikitext;
}
public ArrayList<String> getAutoAddCategories() {
if(!this.isParsed) {
this.parseConfig();
}
return autoAddCategories;
}
@ -35,20 +50,32 @@ public class Campaign {
}
public String getOwnWorkLicenseDefault() {
if(!this.isParsed) {
this.parseConfig();
}
return ownWorkLicenseDefault;
}
public String getDefaultDescription() {
if(!this.isParsed) {
this.parseConfig();
}
return defaultDescription;
}
public JSONObject getConfig() {
if(!this.isParsed) {
this.parseConfig();
}
return config;
}
public Campaign(String name, JSONObject config) {
this.config = config;
this.name = name;
private void parseConfig() {
try {
this.config = new JSONObject(body);
} catch (JSONException e) {
throw new RuntimeException(e); // because what else are you gonna do?
}
if(config.has("autoAdd")) {
this.autoAddWikitext = config.optJSONObject("autoAdd").optString("wikitext", null);
if(config.optJSONObject("autoAdd").has("categories")) {
@ -59,5 +86,104 @@ public class Campaign {
}
}
}
this.title = config.optString("title", name);
this.description = config.optString("description", "");
this.isParsed = true;
}
private Campaign(String name, String body, String trackingCategory) {
this.name = name;
this.body = body;
this.trackingCategory = trackingCategory;
}
public ContentValues toContentValues() {
ContentValues cv = new ContentValues();
cv.put(Table.COLUMN_NAME, this.getName());
cv.put(Table.COLUMN_ENABLED, this.isEnabled() ? 1 : 0);
cv.put(Table.COLUMN_TITLE, this.getTitle());
cv.put(Table.COLUMN_DESCRIPTION, this.getDescription());
cv.put(Table.COLUMN_TRACKING_CATEGORY, this.getTrackingCategory());
cv.put(Table.COLUMN_BODY, this.body);
return cv;
}
public static Campaign parse(String name, String body, String trackingCategory) {
Campaign c = new Campaign(name, body, trackingCategory);
c.parseConfig();
return c;
}
public static Campaign fromCursor(Cursor cursor) {
String name = cursor.getString(1);
Boolean enabled = cursor.getInt(2) == 1;
String title = cursor.getString(3);
String description = cursor.getString(4);
String trackingCategory = cursor.getString(5);
String body = cursor.getString(6);
Campaign c = new Campaign(name, body, trackingCategory);
c.title = title;
c.description = description;
c.enabled = enabled;
return c;
}
public String getTrackingCategory() {
return trackingCategory;
}
public String getDescription() {
return description;
}
public String getTitle() {
return title;
}
public static class Table {
public static final String TABLE_NAME = "campaigns";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_NAME = "name";
public static final String COLUMN_ENABLED = "enabled";
public static final String COLUMN_TITLE = "title";
public static final String COLUMN_DESCRIPTION = "description";
public static final String COLUMN_TRACKING_CATEGORY = "tracking_category";
public static final String COLUMN_BODY = "body";
// NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
public static final String[] ALL_FIELDS = {
COLUMN_ID,
COLUMN_NAME,
COLUMN_ENABLED,
COLUMN_TITLE,
COLUMN_DESCRIPTION,
COLUMN_DESCRIPTION,
COLUMN_TRACKING_CATEGORY,
COLUMN_BODY
};
private static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " ("
+ "_id INTEGER PRIMARY KEY,"
+ "name STRING,"
+ "enabled INTEGER,"
+ "title STRING,"
+ "description STRING,"
+ "tracking_category STRING,"
+ "body STRING"
+ ");";
public static void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_STATEMENT);
}
public static void onUpdate(SQLiteDatabase db, int from, int to) {
if(to <= 6) {
onCreate(db);
return;
}
return;
}
}
}

View file

@ -1,15 +1,44 @@
package org.wikimedia.commons.campaigns;
import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.widget.ListView;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import org.wikimedia.commons.R;
public class CampaignActivity extends SherlockFragmentActivity {
public class CampaignActivity
extends SherlockFragmentActivity
implements LoaderManager.LoaderCallbacks<Cursor> {
private ListView campaignsListView;
private CampaignsListAdapter campaignsListAdapter;
private Cursor allCampaigns;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_campaigns);
campaignsListView = (ListView) findViewById(R.id.campaignsList);
getSupportLoaderManager().initLoader(0, null, this);
}
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
return new CursorLoader(this, CampaignsContentProvider.BASE_URI, Campaign.Table.ALL_FIELDS, "", null, "");
}
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
if(campaignsListAdapter == null) {
campaignsListAdapter = new CampaignsListAdapter(this, cursor, 0);
campaignsListView.setAdapter(campaignsListAdapter);
} else {
campaignsListAdapter.swapCursor(cursor);
}
}
public void onLoaderReset(Loader<Cursor> cursorLoader) {
campaignsListAdapter.swapCursor(null);
}
}

View file

@ -0,0 +1,206 @@
package org.wikimedia.commons.campaigns;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import org.wikimedia.commons.CommonsApplication;
import org.wikimedia.commons.data.DBOpenHelper;
public class CampaignsContentProvider extends ContentProvider{
private static final int CAMPAIGNS = 1;
private static final int CAMPAIGNS_ID = 2;
public static final String AUTHORITY = "org.wikimedia.commons.campaigns.contentprovider";
private static final String BASE_PATH = "campiagns";
public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
uriMatcher.addURI(AUTHORITY, BASE_PATH, CAMPAIGNS);
uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", CAMPAIGNS_ID);
}
public static Uri uriForId(int id) {
return Uri.parse(BASE_URI.toString() + "/" + id);
}
private DBOpenHelper dbOpenHelper;
@Override
public boolean onCreate() {
dbOpenHelper = ((CommonsApplication)this.getContext().getApplicationContext()).getDbOpenHelper();
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(Campaign.Table.TABLE_NAME);
int uriType = uriMatcher.match(uri);
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
Cursor cursor;
switch(uriType) {
case CAMPAIGNS:
cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
break;
case CAMPAIGNS_ID:
cursor = queryBuilder.query(db,
Campaign.Table.ALL_FIELDS,
"_id = ?",
new String[] { uri.getLastPathSegment() },
null,
null,
sortOrder
);
break;
default:
throw new IllegalArgumentException("Unknown URI" + uri);
}
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues contentValues) {
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
long id = 0;
switch (uriType) {
case CAMPAIGNS:
sqlDB.beginTransaction();
// if the campaign already exists, rip it out and then re-insert
if(campaignExists(sqlDB, contentValues)) {
sqlDB.delete(
Campaign.Table.TABLE_NAME,
Campaign.Table.COLUMN_NAME + " = ?",
new String[]{contentValues.getAsString(Campaign.Table.COLUMN_NAME)}
);
}
id = sqlDB.insert(Campaign.Table.TABLE_NAME, null, contentValues);
sqlDB.endTransaction();
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return Uri.parse(BASE_URI + "/" + id);
}
@Override
public int delete(Uri uri, String s, String[] strings) {
int rows = 0;
int uriType = uriMatcher.match(uri);
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
switch(uriType) {
case CAMPAIGNS_ID:
rows = db.delete(Campaign.Table.TABLE_NAME,
"_id = ?",
new String[] { uri.getLastPathSegment() }
);
break;
default:
throw new IllegalArgumentException("Unknown URI" + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return rows;
}
private boolean campaignExists(SQLiteDatabase db, ContentValues campaign) {
Cursor cr = db.query(
Campaign.Table.TABLE_NAME,
new String[]{Campaign.Table.COLUMN_NAME},
Campaign.Table.COLUMN_NAME + " = ?",
new String[]{campaign.getAsString(Campaign.Table.COLUMN_NAME)},
"", "", ""
);
return cr != null && cr.getCount() != 0;
}
@Override
public int bulkInsert(Uri uri, ContentValues[] values) {
Log.d("Commons", "Hello, bulk insert!");
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
sqlDB.beginTransaction();
switch (uriType) {
case CAMPAIGNS:
for(ContentValues value: values) {
Log.d("Commons", "Inserting! " + value.toString());
// if the campaign already exists, rip it out and then re-insert
if(campaignExists(sqlDB, value)) {
sqlDB.delete(
Campaign.Table.TABLE_NAME,
Campaign.Table.COLUMN_NAME + " = ?",
new String[]{value.getAsString(Campaign.Table.COLUMN_NAME)}
);
}
sqlDB.insert(Campaign.Table.TABLE_NAME, null, value);
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
sqlDB.setTransactionSuccessful();
sqlDB.endTransaction();
getContext().getContentResolver().notifyChange(uri, null);
return values.length;
}
@Override
public int update(Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) {
/*
SQL Injection warnings: First, note that we're not exposing this to the outside world (exported="false")
Even then, we should make sure to sanitize all user input appropriately. Input that passes through ContentValues
should be fine. So only issues are those that pass in via concating.
In here, the only concat created argument is for id. It is cast to an int, and will error out otherwise.
*/
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
int rowsUpdated = 0;
switch (uriType) {
case CAMPAIGNS:
rowsUpdated = sqlDB.update(Campaign.Table.TABLE_NAME,
contentValues,
selection,
selectionArgs);
break;
case CAMPAIGNS_ID:
int id = Integer.valueOf(uri.getLastPathSegment());
if (TextUtils.isEmpty(selection)) {
rowsUpdated = sqlDB.update(Campaign.Table.TABLE_NAME,
contentValues,
Campaign.Table.COLUMN_ID + " = ?",
new String[] { String.valueOf(id) } );
} else {
throw new IllegalArgumentException("Parameter `selection` should be empty when updating an ID");
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri + " with type " + uriType);
}
getContext().getContentResolver().notifyChange(uri, null);
return rowsUpdated;
}
}

View file

@ -1,54 +1,47 @@
package org.wikimedia.commons.campaigns;
import android.app.Activity;
import android.view.*;
import android.widget.*;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.support.v4.widget.CursorAdapter;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.actionbarsherlock.app.SherlockFragment;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.assist.SimpleImageLoadingListener;
import org.wikimedia.commons.CommonsApplication;
import org.wikimedia.commons.MediaWikiImageView;
import org.wikimedia.commons.R;
import org.wikimedia.commons.Utils;
import org.wikimedia.commons.campaigns.Campaign;
import java.util.ArrayList;
class CampaignsListAdapter extends CursorAdapter {
public class CampaignsListAdapter extends BaseAdapter {
private ArrayList<Campaign> campaigns;
private DisplayImageOptions contributionDisplayOptions = Utils.getGenericDisplayOptions().build();;
private Activity activity;
public CampaignsListAdapter(Activity activity, ArrayList<Campaign> campaigns) {
this.campaigns = campaigns;
public CampaignsListAdapter(Activity activity, Cursor c, int flags) {
super(activity, c, flags);
this.activity = activity;
}
public ArrayList<Campaign> getCampaigns() {
return campaigns;
@Override
public View newView(Context context, Cursor cursor, ViewGroup viewGroup) {
View parent = activity.getLayoutInflater().inflate(android.R.layout.simple_list_item_1, viewGroup, false);
return parent;
}
public void setCampaigns(ArrayList<Campaign> campaigns) {
this.campaigns = campaigns;
@Override
public void bindView(View view, Context context, Cursor cursor) {
TextView campaignName = (TextView)view.findViewById(android.R.id.text1);
Campaign campaign = Campaign.fromCursor(cursor);
campaignName.setText(campaign.getTitle());
}
public int getCount() {
if(campaigns == null) {
return 0;
}
return campaigns.size();
}
public Object getItem(int i) {
return campaigns.get(i);
}
public long getItemId(int i) {
return i;
}
public View getView(int i, View view, ViewGroup viewGroup) {
if(view == null) {
view = activity.getLayoutInflater().inflate(R.layout.layout_campaign_item, null);
}
TextView campaignName = (TextView)view.findViewById(R.id.campaignItemName);
Campaign campaign = campaigns.get(i);
campaignName.setText(campaign.getName());
return view;
}
}

View file

@ -1,39 +0,0 @@
package org.wikimedia.commons.campaigns;
import android.os.*;
import android.view.*;
import android.widget.*;
import com.actionbarsherlock.app.SherlockFragment;
import org.wikimedia.commons.*;
import java.util.*;
public class CampaignsListFragment extends SherlockFragment {
private ArrayList<Campaign> campaigns;
private CampaignsListAdapter listAdapter;
private GridView campaignsListView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_campaigns, container, false);
campaignsListView = (GridView)view.findViewById(R.id.campaignsList);
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
listAdapter = new CampaignsListAdapter(getActivity(), campaigns);
campaignsListView.setAdapter(listAdapter);
Utils.executeAsyncTask(new FetchCampaignsTask(getActivity()){
@Override
protected void onPostExecute(ArrayList<Campaign> campaigns) {
super.onPostExecute(campaigns);
CampaignsListFragment.this.campaigns = campaigns;
listAdapter.setCampaigns(campaigns);
listAdapter.notifyDataSetChanged();
}
});
}
}

View file

@ -0,0 +1,95 @@
package org.wikimedia.commons.campaigns;
import android.accounts.Account;
import android.content.*;
import android.database.Cursor;
import android.os.Bundle;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import org.mediawiki.api.ApiResult;
import org.mediawiki.api.MWApi;
import org.wikimedia.commons.CommonsApplication;
import org.wikimedia.commons.Utils;
import org.wikimedia.commons.contributions.Contribution;
import org.wikimedia.commons.contributions.ContributionsContentProvider;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
public class CampaignsSyncAdapter extends AbstractThreadedSyncAdapter {
private static int COMMIT_THRESHOLD = 10;
public CampaignsSyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
}
private int getLimit() {
return 500; // FIXME: Parameterize!
}
@Override
public void onPerformSync(Account account, Bundle bundle, String s, ContentProviderClient contentProviderClient, SyncResult syncResult) {
// This code is fraught with possibilities of race conditions, but lalalalala I can't hear you!
String user = account.name;
MWApi api = CommonsApplication.createMWApi();
ApiResult result;
Boolean done = false;
String queryContinue = null;
while(!done) {
try {
MWApi.RequestBuilder builder = api.action("query")
.param("list", "allcampaigns")
// Disabled, since we want to modify local state if the campaign was disabled
// FIXME: To be more effecient, delete the disabled campaigns locally
//.param("ucenabledonly", "true")
.param("uclimit", getLimit());
if(!TextUtils.isEmpty(queryContinue)) {
builder.param("uccontinue", queryContinue);
}
result = builder.get();
} catch (IOException e) {
// There isn't really much we can do, eh?
// FIXME: Perhaps add EventLogging?
syncResult.stats.numIoExceptions += 1; // Not sure if this does anything. Shitty docs
Log.d("Commons", "Syncing failed due to " + e.toString());
return;
}
ArrayList<ApiResult> campaigns = result.getNodes("/api/query/allcampaigns/campaign");
Log.d("Commons", campaigns.size() + " results!");
ArrayList<ContentValues> campaignValues = new ArrayList<ContentValues>();
for(ApiResult campaignItem: campaigns) {
String name = campaignItem.getString("@name");
String body = campaignItem.getString(".");
Log.d("Commons", "Campaign body is " + body);
String trackingCat = campaignItem.getString("@trackingCategory");
Campaign campaign = Campaign.parse(name, body, trackingCat);
campaignValues.add(campaign.toContentValues());
if(campaignValues.size() % COMMIT_THRESHOLD == 0) {
try {
contentProviderClient.bulkInsert(CampaignsContentProvider.BASE_URI, campaignValues.toArray(new ContentValues[]{}));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
campaignValues.clear();
}
}
if(campaignValues.size() != 0) {
try {
contentProviderClient.bulkInsert(CampaignsContentProvider.BASE_URI, campaignValues.toArray(new ContentValues[]{}));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
queryContinue = result.getString("/api/query-continue/allcampaigns/@uccontinue");
if(TextUtils.isEmpty(queryContinue)) {
done = true;
}
}
}
}

View file

@ -0,0 +1,27 @@
package org.wikimedia.commons.campaigns;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import org.wikimedia.commons.contributions.ContributionsSyncAdapter;
public class CampaignsSyncService extends Service {
private static final Object sSyncAdapterLock = new Object();
private static CampaignsSyncAdapter sSyncAdapter = null;
@Override
public void onCreate() {
synchronized (sSyncAdapterLock) {
if (sSyncAdapter == null) {
sSyncAdapter = new CampaignsSyncAdapter(getApplicationContext(), true);
}
}
}
@Override
public IBinder onBind(Intent intent) {
return sSyncAdapter.getSyncAdapterBinder();
}
}

View file

@ -1,52 +0,0 @@
package org.wikimedia.commons.campaigns;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import org.mediawiki.api.ApiResult;
import org.mediawiki.api.MWApi;
import org.wikimedia.commons.CommonsApplication;
import org.wikimedia.commons.Utils;
import java.io.IOException;
import java.util.ArrayList;
public class FetchCampaignsTask extends AsyncTask<Void, Void, ArrayList<Campaign>>{
private Context context;
private MWApi api;
public FetchCampaignsTask(Context context) {
this.context = context;
this.api = ((CommonsApplication)context.getApplicationContext()).getApi();
}
@Override
protected ArrayList<Campaign> doInBackground(Void... voids) {
ArrayList<Campaign> campaigns = new ArrayList<Campaign>();
ApiResult result;
try {
result = api.action("query").param("prop", "revisions")
.param("rvprop", "content").param("generator", "allpages")
.param("gapnamespace", 460).param("gaplimit", 500).get(); //FIXME: Actually paginate!
} catch (IOException e) {
throw new RuntimeException(e); // FIXME: Do something else ;)
}
ArrayList<ApiResult> pageNodes = result.getNodes("/api/query/pages/page");
for(ApiResult pageNode : pageNodes) {
Log.d("Commons", Utils.getStringFromDOM(pageNode.getDocument()));
String configString = pageNode.getString("revisions/rev");
String pageName = pageNode.getString("@title").split(":")[1];
JSONObject config;
try {
config = new JSONObject(configString);
} catch (JSONException e) {
throw new RuntimeException(e); // NEVER HAPPENS!
}
campaigns.add(new Campaign(pageName, config));
}
return campaigns;
}
}

View file

@ -3,6 +3,7 @@ package org.wikimedia.commons.data;
import android.content.*;
import android.database.sqlite.*;
import org.wikimedia.commons.campaigns.Campaign;
import org.wikimedia.commons.category.Category;
import org.wikimedia.commons.contributions.*;
import org.wikimedia.commons.modifications.ModifierSequence;
@ -21,6 +22,7 @@ public class DBOpenHelper extends SQLiteOpenHelper{
Contribution.Table.onCreate(sqLiteDatabase);
ModifierSequence.Table.onCreate(sqLiteDatabase);
Category.Table.onCreate(sqLiteDatabase);
Campaign.Table.onCreate(sqLiteDatabase);
}
@Override
@ -28,5 +30,6 @@ public class DBOpenHelper extends SQLiteOpenHelper{
Contribution.Table.onUpdate(sqLiteDatabase, from, to);
ModifierSequence.Table.onUpdate(sqLiteDatabase, from, to);
Category.Table.onUpdate(sqLiteDatabase, from, to);
Campaign.Table.onUpdate(sqLiteDatabase, from, to);
}
}