mirror of
				https://github.com/commons-app/apps-android-commons.git
				synced 2025-10-30 22:34:02 +01:00 
			
		
		
		
	Synced branch with master
This commit is contained in:
		
						commit
						4fc3040d52
					
				
					 479 changed files with 11666 additions and 3806 deletions
				
			
		|  | @ -1,13 +1,8 @@ | |||
| package fr.free.nrw.commons.contributions; | ||||
| 
 | ||||
| import android.content.ContentProviderClient; | ||||
| import android.content.ContentValues; | ||||
| import android.database.Cursor; | ||||
| import android.database.sqlite.SQLiteDatabase; | ||||
| import android.net.Uri; | ||||
| import android.os.Parcel; | ||||
| import android.os.RemoteException; | ||||
| import android.text.TextUtils; | ||||
| import android.support.annotation.NonNull; | ||||
| 
 | ||||
| import java.text.SimpleDateFormat; | ||||
| import java.util.Date; | ||||
|  | @ -16,7 +11,6 @@ import java.util.Locale; | |||
| import fr.free.nrw.commons.BuildConfig; | ||||
| import fr.free.nrw.commons.CommonsApplication; | ||||
| import fr.free.nrw.commons.Media; | ||||
| import fr.free.nrw.commons.Utils; | ||||
| import fr.free.nrw.commons.settings.Prefs; | ||||
| 
 | ||||
| public class Contribution extends Media { | ||||
|  | @ -43,7 +37,6 @@ public class Contribution extends Media { | |||
|     public static final String SOURCE_GALLERY = "gallery"; | ||||
|     public static final String SOURCE_EXTERNAL = "external"; | ||||
| 
 | ||||
|     private ContentProviderClient client; | ||||
|     private Uri contentUri; | ||||
|     private String source; | ||||
|     private String editSummary; | ||||
|  | @ -51,24 +44,42 @@ public class Contribution extends Media { | |||
|     private int state; | ||||
|     private long transferred; | ||||
|     private String decimalCoords; | ||||
| 
 | ||||
|     private boolean isMultiple; | ||||
| 
 | ||||
|     public boolean getMultiple() { | ||||
|         return isMultiple; | ||||
|     public Contribution(Uri contentUri, String filename, Uri localUri, String imageUrl, Date timestamp, | ||||
|                         int state, long dataLength, Date dateUploaded, long transferred, | ||||
|                         String source, String description, String creator, boolean isMultiple, | ||||
|                         int width, int height, String license) { | ||||
|         super(localUri, imageUrl, filename, description, dataLength, timestamp, dateUploaded, creator); | ||||
|         this.contentUri = contentUri; | ||||
|         this.state = state; | ||||
|         this.timestamp = timestamp; | ||||
|         this.transferred = transferred; | ||||
|         this.source = source; | ||||
|         this.isMultiple = isMultiple; | ||||
|         this.width = width; | ||||
|         this.height = height; | ||||
|         this.license = license; | ||||
|     } | ||||
| 
 | ||||
|     public void setMultiple(boolean multiple) { | ||||
|         isMultiple = multiple; | ||||
|     } | ||||
| 
 | ||||
|     public Contribution(Uri localUri, String remoteUri, String filename, String description, long dataLength, Date dateCreated, Date dateUploaded, String creator, String editSummary, String decimalCoords) { | ||||
|         super(localUri, remoteUri, filename, description, dataLength, dateCreated, dateUploaded, creator); | ||||
|     public Contribution(Uri localUri, String imageUrl, String filename, String description, long dataLength, | ||||
|                         Date dateCreated, Date dateUploaded, String creator, String editSummary, String decimalCoords) { | ||||
|         super(localUri, imageUrl, filename, description, dataLength, dateCreated, dateUploaded, creator); | ||||
|         this.decimalCoords = decimalCoords; | ||||
|         this.editSummary = editSummary; | ||||
|         timestamp = new Date(System.currentTimeMillis()); | ||||
|     } | ||||
| 
 | ||||
|     public Contribution(Parcel in) { | ||||
|         super(in); | ||||
|         contentUri = in.readParcelable(Uri.class.getClassLoader()); | ||||
|         source = in.readString(); | ||||
|         timestamp = (Date) in.readSerializable(); | ||||
|         state = in.readInt(); | ||||
|         transferred = in.readLong(); | ||||
|         isMultiple = in.readInt() == 1; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void writeToParcel(Parcel parcel, int flags) { | ||||
|         super.writeToParcel(parcel, flags); | ||||
|  | @ -80,14 +91,12 @@ public class Contribution extends Media { | |||
|         parcel.writeInt(isMultiple ? 1 : 0); | ||||
|     } | ||||
| 
 | ||||
|     public Contribution(Parcel in) { | ||||
|         super(in); | ||||
|         contentUri = in.readParcelable(Uri.class.getClassLoader()); | ||||
|         source = in.readString(); | ||||
|         timestamp = (Date) in.readSerializable(); | ||||
|         state = in.readInt(); | ||||
|         transferred = in.readLong(); | ||||
|         isMultiple = in.readInt() == 1; | ||||
|     public boolean getMultiple() { | ||||
|         return isMultiple; | ||||
|     } | ||||
| 
 | ||||
|     public void setMultiple(boolean multiple) { | ||||
|         isMultiple = multiple; | ||||
|     } | ||||
| 
 | ||||
|     public long getTransferred() { | ||||
|  | @ -106,10 +115,18 @@ public class Contribution extends Media { | |||
|         return contentUri; | ||||
|     } | ||||
| 
 | ||||
|     public void setContentUri(Uri contentUri) { | ||||
|         this.contentUri = contentUri; | ||||
|     } | ||||
| 
 | ||||
|     public Date getTimestamp() { | ||||
|         return timestamp; | ||||
|     } | ||||
| 
 | ||||
|     public void setTimestamp(Date timestamp) { | ||||
|         this.timestamp = timestamp; | ||||
|     } | ||||
| 
 | ||||
|     public int getState() { | ||||
|         return state; | ||||
|     } | ||||
|  | @ -149,68 +166,12 @@ public class Contribution extends Media { | |||
|         } | ||||
| 
 | ||||
|         buffer.append("== {{int:license-header}} ==\n") | ||||
|                 .append(Utils.licenseTemplateFor(getLicense())).append("\n\n") | ||||
|                 .append(licenseTemplateFor(getLicense())).append("\n\n") | ||||
|                 .append("{{Uploaded from Mobile|platform=Android|version=").append(BuildConfig.VERSION_NAME).append("}}\n") | ||||
|                 .append(getTrackingTemplates()); | ||||
|         return buffer.toString(); | ||||
|     } | ||||
| 
 | ||||
|     public void setContentProviderClient(ContentProviderClient client) { | ||||
|         this.client = client; | ||||
|     } | ||||
| 
 | ||||
|     public void save() { | ||||
|         try { | ||||
|             if (contentUri == null) { | ||||
|                 contentUri = client.insert(ContributionsContentProvider.BASE_URI, this.toContentValues()); | ||||
|             } else { | ||||
|                 client.update(contentUri, toContentValues(), null, null); | ||||
|             } | ||||
|         } catch (RemoteException e) { | ||||
|             throw new RuntimeException(e); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public void delete() { | ||||
|         try { | ||||
|             if (contentUri == null) { | ||||
|                 // noooo | ||||
|                 throw new RuntimeException("tried to delete item with no content URI"); | ||||
|             } else { | ||||
|                 client.delete(contentUri, null, null); | ||||
|             } | ||||
|         } catch (RemoteException e) { | ||||
|             throw new RuntimeException(e); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public ContentValues toContentValues() { | ||||
|         ContentValues cv = new ContentValues(); | ||||
|         cv.put(Table.COLUMN_FILENAME, getFilename()); | ||||
|         if (getLocalUri() != null) { | ||||
|             cv.put(Table.COLUMN_LOCAL_URI, getLocalUri().toString()); | ||||
|         } | ||||
|         if (getImageUrl() != null) { | ||||
|             cv.put(Table.COLUMN_IMAGE_URL, getImageUrl()); | ||||
|         } | ||||
|         if (getDateUploaded() != null) { | ||||
|             cv.put(Table.COLUMN_UPLOADED, getDateUploaded().getTime()); | ||||
|         } | ||||
|         cv.put(Table.COLUMN_LENGTH, getDataLength()); | ||||
|         cv.put(Table.COLUMN_TIMESTAMP, getTimestamp().getTime()); | ||||
|         cv.put(Table.COLUMN_STATE, getState()); | ||||
|         cv.put(Table.COLUMN_TRANSFERRED, transferred); | ||||
|         cv.put(Table.COLUMN_SOURCE, source); | ||||
|         cv.put(Table.COLUMN_DESCRIPTION, description); | ||||
|         cv.put(Table.COLUMN_CREATOR, creator); | ||||
|         cv.put(Table.COLUMN_MULTIPLE, isMultiple ? 1 : 0); | ||||
|         cv.put(Table.COLUMN_WIDTH, width); | ||||
|         cv.put(Table.COLUMN_HEIGHT, height); | ||||
|         cv.put(Table.COLUMN_LICENSE, license); | ||||
|         return cv; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void setFilename(String filename) { | ||||
|         this.filename = filename; | ||||
|  | @ -224,33 +185,6 @@ public class Contribution extends Media { | |||
|         timestamp = new Date(System.currentTimeMillis()); | ||||
|     } | ||||
| 
 | ||||
|     public static Contribution fromCursor(Cursor cursor) { | ||||
|         // Hardcoding column positions! | ||||
|         Contribution c = new Contribution(); | ||||
| 
 | ||||
|         //Check that cursor has a value to avoid CursorIndexOutOfBoundsException | ||||
|         if (cursor.getCount() > 0) { | ||||
|             c.contentUri = ContributionsContentProvider.uriForId(cursor.getInt(0)); | ||||
|             c.filename = cursor.getString(1); | ||||
|             c.localUri = TextUtils.isEmpty(cursor.getString(2)) ? null : Uri.parse(cursor.getString(2)); | ||||
|             c.imageUrl = cursor.getString(3); | ||||
|             c.timestamp = cursor.getLong(4) == 0 ? null : new Date(cursor.getLong(4)); | ||||
|             c.state = cursor.getInt(5); | ||||
|             c.dataLength = cursor.getLong(6); | ||||
|             c.dateUploaded = cursor.getLong(7) == 0 ? null : new Date(cursor.getLong(7)); | ||||
|             c.transferred = cursor.getLong(8); | ||||
|             c.source = cursor.getString(9); | ||||
|             c.description = cursor.getString(10); | ||||
|             c.creator = cursor.getString(11); | ||||
|             c.isMultiple = cursor.getInt(12) == 1; | ||||
|             c.width = cursor.getInt(13); | ||||
|             c.height = cursor.getInt(14); | ||||
|             c.license = cursor.getString(15); | ||||
|         } | ||||
| 
 | ||||
|         return c; | ||||
|     } | ||||
| 
 | ||||
|     public String getSource() { | ||||
|         return source; | ||||
|     } | ||||
|  | @ -267,118 +201,25 @@ public class Contribution extends Media { | |||
|         this.decimalCoords = decimalCoords; | ||||
|     } | ||||
| 
 | ||||
|     public static class Table { | ||||
|         public static final String TABLE_NAME = "contributions"; | ||||
| 
 | ||||
|         public static final String COLUMN_ID = "_id"; | ||||
|         public static final String COLUMN_FILENAME = "filename"; | ||||
|         public static final String COLUMN_LOCAL_URI = "local_uri"; | ||||
|         public static final String COLUMN_IMAGE_URL = "image_url"; | ||||
|         public static final String COLUMN_TIMESTAMP = "timestamp"; | ||||
|         public static final String COLUMN_STATE = "state"; | ||||
|         public static final String COLUMN_LENGTH = "length"; | ||||
|         public static final String COLUMN_UPLOADED = "uploaded"; | ||||
|         public static final String COLUMN_TRANSFERRED = "transferred"; // Currently transferred number of bytes | ||||
|         public static final String COLUMN_SOURCE = "source"; | ||||
|         public static final String COLUMN_DESCRIPTION = "description"; | ||||
|         public static final String COLUMN_CREATOR = "creator"; // Initial uploader | ||||
|         public static final String COLUMN_MULTIPLE = "multiple"; | ||||
|         public static final String COLUMN_WIDTH = "width"; | ||||
|         public static final String COLUMN_HEIGHT = "height"; | ||||
|         public static final String COLUMN_LICENSE = "license"; | ||||
| 
 | ||||
|         // 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_FILENAME, | ||||
|                 COLUMN_LOCAL_URI, | ||||
|                 COLUMN_IMAGE_URL, | ||||
|                 COLUMN_TIMESTAMP, | ||||
|                 COLUMN_STATE, | ||||
|                 COLUMN_LENGTH, | ||||
|                 COLUMN_UPLOADED, | ||||
|                 COLUMN_TRANSFERRED, | ||||
|                 COLUMN_SOURCE, | ||||
|                 COLUMN_DESCRIPTION, | ||||
|                 COLUMN_CREATOR, | ||||
|                 COLUMN_MULTIPLE, | ||||
|                 COLUMN_WIDTH, | ||||
|                 COLUMN_HEIGHT, | ||||
|                 COLUMN_LICENSE | ||||
|         }; | ||||
| 
 | ||||
| 
 | ||||
|         private static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " (" | ||||
|                 + "_id INTEGER PRIMARY KEY," | ||||
|                 + "filename STRING," | ||||
|                 + "local_uri STRING," | ||||
|                 + "image_url STRING," | ||||
|                 + "uploaded INTEGER," | ||||
|                 + "timestamp INTEGER," | ||||
|                 + "state INTEGER," | ||||
|                 + "length INTEGER," | ||||
|                 + "transferred INTEGER," | ||||
|                 + "source STRING," | ||||
|                 + "description STRING," | ||||
|                 + "creator STRING," | ||||
|                 + "multiple INTEGER," | ||||
|                 + "width INTEGER," | ||||
|                 + "height INTEGER," | ||||
|                 + "LICENSE STRING" | ||||
|                 + ");"; | ||||
| 
 | ||||
| 
 | ||||
|         public static void onCreate(SQLiteDatabase db) { | ||||
|             db.execSQL(CREATE_TABLE_STATEMENT); | ||||
|     @NonNull | ||||
|     private String licenseTemplateFor(String license) { | ||||
|         switch (license) { | ||||
|             case Prefs.Licenses.CC_BY_3: | ||||
|                 return "{{self|cc-by-3.0}}"; | ||||
|             case Prefs.Licenses.CC_BY_4: | ||||
|                 return "{{self|cc-by-4.0}}"; | ||||
|             case Prefs.Licenses.CC_BY_SA_3: | ||||
|                 return "{{self|cc-by-sa-3.0}}"; | ||||
|             case Prefs.Licenses.CC_BY_SA_4: | ||||
|                 return "{{self|cc-by-sa-4.0}}"; | ||||
|             case Prefs.Licenses.CC0: | ||||
|                 return "{{self|cc-zero}}"; | ||||
|             case Prefs.Licenses.CC_BY: | ||||
|                 return "{{self|cc-by-3.0}}"; | ||||
|             case Prefs.Licenses.CC_BY_SA: | ||||
|                 return "{{self|cc-by-sa-3.0}}"; | ||||
|         } | ||||
| 
 | ||||
|         public static void onDelete(SQLiteDatabase db) { | ||||
|             db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); | ||||
|             onCreate(db); | ||||
|         } | ||||
| 
 | ||||
|         public static void onUpdate(SQLiteDatabase db, int from, int to) { | ||||
|             if (from == to) { | ||||
|                 return; | ||||
|             } | ||||
|             if (from == 1) { | ||||
|                 db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN description STRING;"); | ||||
|                 db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN creator STRING;"); | ||||
|                 from++; | ||||
|                 onUpdate(db, from, to); | ||||
|                 return; | ||||
|             } | ||||
|             if (from == 2) { | ||||
|                 db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN multiple INTEGER;"); | ||||
|                 db.execSQL("UPDATE " + TABLE_NAME + " SET multiple = 0"); | ||||
|                 from++; | ||||
|                 onUpdate(db, from, to); | ||||
|                 return; | ||||
|             } | ||||
|             if (from == 3) { | ||||
|                 // Do nothing | ||||
|                 from++; | ||||
|                 onUpdate(db, from, to); | ||||
|                 return; | ||||
|             } | ||||
|             if (from == 4) { | ||||
|                 // Do nothing -- added Category | ||||
|                 from++; | ||||
|                 onUpdate(db, from, to); | ||||
|                 return; | ||||
|             } | ||||
|             if (from == 5) { | ||||
|                 // Added width and height fields | ||||
|                 db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN width INTEGER;"); | ||||
|                 db.execSQL("UPDATE " + TABLE_NAME + " SET width = 0"); | ||||
|                 db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN height INTEGER;"); | ||||
|                 db.execSQL("UPDATE " + TABLE_NAME + " SET height = 0"); | ||||
|                 db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN license STRING;"); | ||||
|                 db.execSQL("UPDATE " + TABLE_NAME + " SET license='" + Prefs.Licenses.CC_BY_SA_3 + "';"); | ||||
|                 from++; | ||||
|                 onUpdate(db, from, to); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|         throw new RuntimeException("Unrecognized license value: " + license); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -93,10 +93,15 @@ class ContributionController { | |||
|                 shareIntent.putExtra(EXTRA_SOURCE, SOURCE_GALLERY); | ||||
|                 break; | ||||
|             case SELECT_FROM_CAMERA: | ||||
|                 shareIntent.setType("image/jpeg"); //FIXME: Find out appropriate mime type | ||||
|                 //FIXME: Find out appropriate mime type | ||||
|                 // AFAIK this is the right type for a JPEG image | ||||
|                 // https://developer.android.com/training/sharing/send.html#send-binary-content | ||||
|                 shareIntent.setType("image/jpeg"); | ||||
|                 shareIntent.putExtra(EXTRA_STREAM, lastGeneratedCaptureUri); | ||||
|                 shareIntent.putExtra(EXTRA_SOURCE, SOURCE_CAMERA); | ||||
|                 break; | ||||
|             default: | ||||
|                 break; | ||||
|         } | ||||
|         Timber.i("Image selected"); | ||||
|         try { | ||||
|  |  | |||
|  | @ -0,0 +1,281 @@ | |||
| package fr.free.nrw.commons.contributions; | ||||
| 
 | ||||
| import android.content.ContentProviderClient; | ||||
| import android.content.ContentValues; | ||||
| import android.database.Cursor; | ||||
| import android.database.sqlite.SQLiteDatabase; | ||||
| import android.net.Uri; | ||||
| import android.os.RemoteException; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.text.TextUtils; | ||||
| 
 | ||||
| import java.util.Date; | ||||
| 
 | ||||
| import javax.inject.Inject; | ||||
| import javax.inject.Named; | ||||
| import javax.inject.Provider; | ||||
| 
 | ||||
| import fr.free.nrw.commons.settings.Prefs; | ||||
| 
 | ||||
| import static fr.free.nrw.commons.contributions.ContributionDao.Table.ALL_FIELDS; | ||||
| import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI; | ||||
| import static fr.free.nrw.commons.contributions.ContributionsContentProvider.uriForId; | ||||
| 
 | ||||
| public class ContributionDao { | ||||
|     /* | ||||
|         This sorts in the following order: | ||||
|         Currently Uploading | ||||
|         Failed (Sorted in ascending order of time added - FIFO) | ||||
|         Queued to Upload (Sorted in ascending order of time added - FIFO) | ||||
|         Completed (Sorted in descending order of time added) | ||||
| 
 | ||||
|         This is why Contribution.STATE_COMPLETED is -1. | ||||
|      */ | ||||
|     static final String CONTRIBUTION_SORT = Table.COLUMN_STATE + " DESC, " | ||||
|             + Table.COLUMN_UPLOADED + " DESC , (" | ||||
|             + Table.COLUMN_TIMESTAMP + " * " | ||||
|             + Table.COLUMN_STATE + ")"; | ||||
| 
 | ||||
|     private final Provider<ContentProviderClient> clientProvider; | ||||
| 
 | ||||
|     @Inject | ||||
|     public ContributionDao(@Named("contribution") Provider<ContentProviderClient> clientProvider) { | ||||
|         this.clientProvider = clientProvider; | ||||
|     } | ||||
| 
 | ||||
|     Cursor loadAllContributions() { | ||||
|         ContentProviderClient db = clientProvider.get(); | ||||
|         try { | ||||
|             return db.query(BASE_URI, ALL_FIELDS, "", null, CONTRIBUTION_SORT); | ||||
|         } catch (RemoteException e) { | ||||
|             return null; | ||||
|         } finally { | ||||
|             db.release(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public void save(Contribution contribution) { | ||||
|         ContentProviderClient db = clientProvider.get(); | ||||
|         try { | ||||
|             if (contribution.getContentUri() == null) { | ||||
|                 contribution.setContentUri(db.insert(BASE_URI, toContentValues(contribution))); | ||||
|             } else { | ||||
|                 db.update(contribution.getContentUri(), toContentValues(contribution), null, null); | ||||
|             } | ||||
|         } catch (RemoteException e) { | ||||
|             throw new RuntimeException(e); | ||||
|         } finally { | ||||
|             db.release(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public void delete(Contribution contribution) { | ||||
|         ContentProviderClient db = clientProvider.get(); | ||||
|         try { | ||||
|             if (contribution.getContentUri() == null) { | ||||
|                 // noooo | ||||
|                 throw new RuntimeException("tried to delete item with no content URI"); | ||||
|             } else { | ||||
|                 db.delete(contribution.getContentUri(), null, null); | ||||
|             } | ||||
|         } catch (RemoteException e) { | ||||
|             throw new RuntimeException(e); | ||||
|         } finally { | ||||
|             db.release(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ContentValues toContentValues(Contribution contribution) { | ||||
|         ContentValues cv = new ContentValues(); | ||||
|         cv.put(Table.COLUMN_FILENAME, contribution.getFilename()); | ||||
|         if (contribution.getLocalUri() != null) { | ||||
|             cv.put(Table.COLUMN_LOCAL_URI, contribution.getLocalUri().toString()); | ||||
|         } | ||||
|         if (contribution.getImageUrl() != null) { | ||||
|             cv.put(Table.COLUMN_IMAGE_URL, contribution.getImageUrl()); | ||||
|         } | ||||
|         if (contribution.getDateUploaded() != null) { | ||||
|             cv.put(Table.COLUMN_UPLOADED, contribution.getDateUploaded().getTime()); | ||||
|         } | ||||
|         cv.put(Table.COLUMN_LENGTH, contribution.getDataLength()); | ||||
|         cv.put(Table.COLUMN_TIMESTAMP, contribution.getTimestamp().getTime()); | ||||
|         cv.put(Table.COLUMN_STATE, contribution.getState()); | ||||
|         cv.put(Table.COLUMN_TRANSFERRED, contribution.getTransferred()); | ||||
|         cv.put(Table.COLUMN_SOURCE, contribution.getSource()); | ||||
|         cv.put(Table.COLUMN_DESCRIPTION, contribution.getDescription()); | ||||
|         cv.put(Table.COLUMN_CREATOR, contribution.getCreator()); | ||||
|         cv.put(Table.COLUMN_MULTIPLE, contribution.getMultiple() ? 1 : 0); | ||||
|         cv.put(Table.COLUMN_WIDTH, contribution.getWidth()); | ||||
|         cv.put(Table.COLUMN_HEIGHT, contribution.getHeight()); | ||||
|         cv.put(Table.COLUMN_LICENSE, contribution.getLicense()); | ||||
|         return cv; | ||||
|     } | ||||
| 
 | ||||
|     public Contribution fromCursor(Cursor cursor) { | ||||
|         // Hardcoding column positions! | ||||
|         //Check that cursor has a value to avoid CursorIndexOutOfBoundsException | ||||
|         if (cursor.getCount() > 0) { | ||||
|             return new Contribution( | ||||
|                     uriForId(cursor.getInt(0)), | ||||
|                     cursor.getString(1), | ||||
|                     parseUri(cursor.getString(2)), | ||||
|                     cursor.getString(3), | ||||
|                     parseTimestamp(cursor.getLong(4)), | ||||
|                     cursor.getInt(5), | ||||
|                     cursor.getLong(6), | ||||
|                     parseTimestamp(cursor.getLong(7)), | ||||
|                     cursor.getLong(8), | ||||
|                     cursor.getString(9), | ||||
|                     cursor.getString(10), | ||||
|                     cursor.getString(11), | ||||
|                     cursor.getInt(12) == 1, | ||||
|                     cursor.getInt(13), | ||||
|                     cursor.getInt(14), | ||||
|                     cursor.getString(15)); | ||||
|         } | ||||
| 
 | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     @Nullable | ||||
|     private static Date parseTimestamp(long timestamp) { | ||||
|         return timestamp == 0 ? null : new Date(timestamp); | ||||
|     } | ||||
| 
 | ||||
|     @Nullable | ||||
|     private static Uri parseUri(String uriString) { | ||||
|         return TextUtils.isEmpty(uriString) ? null : Uri.parse(uriString); | ||||
|     } | ||||
| 
 | ||||
|     public static class Table { | ||||
|         public static final String TABLE_NAME = "contributions"; | ||||
| 
 | ||||
|         public static final String COLUMN_ID = "_id"; | ||||
|         public static final String COLUMN_FILENAME = "filename"; | ||||
|         public static final String COLUMN_LOCAL_URI = "local_uri"; | ||||
|         public static final String COLUMN_IMAGE_URL = "image_url"; | ||||
|         public static final String COLUMN_TIMESTAMP = "timestamp"; | ||||
|         public static final String COLUMN_STATE = "state"; | ||||
|         public static final String COLUMN_LENGTH = "length"; | ||||
|         public static final String COLUMN_UPLOADED = "uploaded"; | ||||
|         public static final String COLUMN_TRANSFERRED = "transferred"; // Currently transferred number of bytes | ||||
|         public static final String COLUMN_SOURCE = "source"; | ||||
|         public static final String COLUMN_DESCRIPTION = "description"; | ||||
|         public static final String COLUMN_CREATOR = "creator"; // Initial uploader | ||||
|         public static final String COLUMN_MULTIPLE = "multiple"; | ||||
|         public static final String COLUMN_WIDTH = "width"; | ||||
|         public static final String COLUMN_HEIGHT = "height"; | ||||
|         public static final String COLUMN_LICENSE = "license"; | ||||
| 
 | ||||
|         // 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_FILENAME, | ||||
|                 COLUMN_LOCAL_URI, | ||||
|                 COLUMN_IMAGE_URL, | ||||
|                 COLUMN_TIMESTAMP, | ||||
|                 COLUMN_STATE, | ||||
|                 COLUMN_LENGTH, | ||||
|                 COLUMN_UPLOADED, | ||||
|                 COLUMN_TRANSFERRED, | ||||
|                 COLUMN_SOURCE, | ||||
|                 COLUMN_DESCRIPTION, | ||||
|                 COLUMN_CREATOR, | ||||
|                 COLUMN_MULTIPLE, | ||||
|                 COLUMN_WIDTH, | ||||
|                 COLUMN_HEIGHT, | ||||
|                 COLUMN_LICENSE | ||||
|         }; | ||||
| 
 | ||||
|         public static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS " + TABLE_NAME; | ||||
| 
 | ||||
|         public static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " (" | ||||
|                 + "_id INTEGER PRIMARY KEY," | ||||
|                 + "filename STRING," | ||||
|                 + "local_uri STRING," | ||||
|                 + "image_url STRING," | ||||
|                 + "uploaded INTEGER," | ||||
|                 + "timestamp INTEGER," | ||||
|                 + "state INTEGER," | ||||
|                 + "length INTEGER," | ||||
|                 + "transferred INTEGER," | ||||
|                 + "source STRING," | ||||
|                 + "description STRING," | ||||
|                 + "creator STRING," | ||||
|                 + "multiple INTEGER," | ||||
|                 + "width INTEGER," | ||||
|                 + "height INTEGER," | ||||
|                 + "LICENSE STRING" | ||||
|                 + ");"; | ||||
| 
 | ||||
|         // Upgrade from version 1 -> | ||||
|         static final String ADD_CREATOR_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN creator STRING;"; | ||||
|         static final String ADD_DESCRIPTION_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN description STRING;"; | ||||
| 
 | ||||
|         // Upgrade from version 2 -> | ||||
|         static final String ADD_MULTIPLE_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN multiple INTEGER;"; | ||||
|         static final String SET_DEFAULT_MULTIPLE = "UPDATE " + TABLE_NAME + " SET multiple = 0"; | ||||
| 
 | ||||
|         // Upgrade from version 5 -> | ||||
|         static final String ADD_WIDTH_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN width INTEGER;"; | ||||
|         static final String SET_DEFAULT_WIDTH = "UPDATE " + TABLE_NAME + " SET width = 0"; | ||||
|         static final String ADD_HEIGHT_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN height INTEGER;"; | ||||
|         static final String SET_DEFAULT_HEIGHT = "UPDATE " + TABLE_NAME + " SET height = 0"; | ||||
|         static final String ADD_LICENSE_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN license STRING;"; | ||||
|         static final String SET_DEFAULT_LICENSE = "UPDATE " + TABLE_NAME + " SET license='" + Prefs.Licenses.CC_BY_SA_3 + "';"; | ||||
| 
 | ||||
| 
 | ||||
|         public static void onCreate(SQLiteDatabase db) { | ||||
|             db.execSQL(CREATE_TABLE_STATEMENT); | ||||
|         } | ||||
| 
 | ||||
|         public static void onDelete(SQLiteDatabase db) { | ||||
|             db.execSQL(DROP_TABLE_STATEMENT); | ||||
|             onCreate(db); | ||||
|         } | ||||
| 
 | ||||
|         public static void onUpdate(SQLiteDatabase db, int from, int to) { | ||||
|             if (from == to) { | ||||
|                 return; | ||||
|             } | ||||
|             if (from == 1) { | ||||
|                 db.execSQL(ADD_DESCRIPTION_FIELD); | ||||
|                 db.execSQL(ADD_CREATOR_FIELD); | ||||
|                 from++; | ||||
|                 onUpdate(db, from, to); | ||||
|                 return; | ||||
|             } | ||||
|             if (from == 2) { | ||||
|                 db.execSQL(ADD_MULTIPLE_FIELD); | ||||
|                 db.execSQL(SET_DEFAULT_MULTIPLE); | ||||
|                 from++; | ||||
|                 onUpdate(db, from, to); | ||||
|                 return; | ||||
|             } | ||||
|             if (from == 3) { | ||||
|                 // Do nothing | ||||
|                 from++; | ||||
|                 onUpdate(db, from, to); | ||||
|                 return; | ||||
|             } | ||||
|             if (from == 4) { | ||||
|                 // Do nothing -- added Category | ||||
|                 from++; | ||||
|                 onUpdate(db, from, to); | ||||
|                 return; | ||||
|             } | ||||
|             if (from == 5) { | ||||
|                 // Added width and height fields | ||||
|                 db.execSQL(ADD_WIDTH_FIELD); | ||||
|                 db.execSQL(SET_DEFAULT_WIDTH); | ||||
|                 db.execSQL(ADD_HEIGHT_FIELD); | ||||
|                 db.execSQL(SET_DEFAULT_HEIGHT); | ||||
|                 db.execSQL(ADD_LICENSE_FIELD); | ||||
|                 db.execSQL(SET_DEFAULT_LICENSE); | ||||
|                 from++; | ||||
|                 onUpdate(db, from, to); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -9,7 +9,6 @@ import android.database.Cursor; | |||
| import android.database.DataSetObserver; | ||||
| import android.os.Bundle; | ||||
| import android.os.IBinder; | ||||
| import android.preference.PreferenceManager; | ||||
| import android.support.v4.app.FragmentManager; | ||||
| import android.support.v4.app.LoaderManager; | ||||
| import android.support.v4.content.CursorLoader; | ||||
|  | @ -23,13 +22,17 @@ import android.widget.AdapterView; | |||
| 
 | ||||
| import java.util.ArrayList; | ||||
| 
 | ||||
| import javax.inject.Inject; | ||||
| import javax.inject.Named; | ||||
| 
 | ||||
| import butterknife.ButterKnife; | ||||
| import fr.free.nrw.commons.CommonsApplication; | ||||
| import fr.free.nrw.commons.HandlerService; | ||||
| import fr.free.nrw.commons.Media; | ||||
| import fr.free.nrw.commons.R; | ||||
| import fr.free.nrw.commons.auth.AuthenticatedActivity; | ||||
| import fr.free.nrw.commons.auth.SessionManager; | ||||
| import fr.free.nrw.commons.media.MediaDetailPagerFragment; | ||||
| import fr.free.nrw.commons.mwapi.MediaWikiApi; | ||||
| import fr.free.nrw.commons.settings.Prefs; | ||||
| import fr.free.nrw.commons.upload.UploadService; | ||||
| import io.reactivex.android.schedulers.AndroidSchedulers; | ||||
|  | @ -39,15 +42,22 @@ import timber.log.Timber; | |||
| 
 | ||||
| import static android.content.ContentResolver.requestSync; | ||||
| import static fr.free.nrw.commons.contributions.Contribution.STATE_FAILED; | ||||
| import static fr.free.nrw.commons.contributions.Contribution.Table.ALL_FIELDS; | ||||
| import static fr.free.nrw.commons.contributions.ContributionsContentProvider.AUTHORITY; | ||||
| import static fr.free.nrw.commons.contributions.ContributionDao.Table.ALL_FIELDS; | ||||
| import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI; | ||||
| import static fr.free.nrw.commons.settings.Prefs.UPLOADS_SHOWING; | ||||
| 
 | ||||
| public class ContributionsActivity extends AuthenticatedActivity | ||||
|         implements LoaderManager.LoaderCallbacks<Cursor>, AdapterView.OnItemClickListener, | ||||
|         MediaDetailPagerFragment.MediaDetailProvider, FragmentManager.OnBackStackChangedListener, | ||||
|         ContributionsListFragment.SourceRefresher { | ||||
| public  class       ContributionsActivity | ||||
|         extends     AuthenticatedActivity | ||||
|         implements  LoaderManager.LoaderCallbacks<Cursor>, | ||||
|                     AdapterView.OnItemClickListener, | ||||
|                     MediaDetailPagerFragment.MediaDetailProvider, | ||||
|                     FragmentManager.OnBackStackChangedListener, | ||||
|                     ContributionsListFragment.SourceRefresher { | ||||
| 
 | ||||
|     @Inject MediaWikiApi mediaWikiApi; | ||||
|     @Inject SessionManager sessionManager; | ||||
|     @Inject @Named("default_preferences") SharedPreferences prefs; | ||||
|     @Inject ContributionDao contributionDao; | ||||
| 
 | ||||
|     private Cursor allContributions; | ||||
|     private ContributionsListFragment contributionsList; | ||||
|  | @ -55,21 +65,6 @@ public class ContributionsActivity extends AuthenticatedActivity | |||
|     private UploadService uploadService; | ||||
|     private boolean isUploadServiceConnected; | ||||
|     private ArrayList<DataSetObserver> observersWaitingForLoad = new ArrayList<>(); | ||||
|     private String CONTRIBUTION_SELECTION = ""; | ||||
| 
 | ||||
|     /* | ||||
|         This sorts in the following order: | ||||
|         Currently Uploading | ||||
|         Failed (Sorted in ascending order of time added - FIFO) | ||||
|         Queued to Upload (Sorted in ascending order of time added - FIFO) | ||||
|         Completed (Sorted in descending order of time added) | ||||
| 
 | ||||
|         This is why Contribution.STATE_COMPLETED is -1. | ||||
|      */ | ||||
|     private String CONTRIBUTION_SORT = Contribution.Table.COLUMN_STATE + " DESC, " | ||||
|             + Contribution.Table.COLUMN_UPLOADED + " DESC , (" | ||||
|             + Contribution.Table.COLUMN_TIMESTAMP + " * " | ||||
|             + Contribution.Table.COLUMN_STATE + ")"; | ||||
| 
 | ||||
|     private CompositeDisposable compositeDisposable = new CompositeDisposable(); | ||||
| 
 | ||||
|  | @ -84,7 +79,7 @@ public class ContributionsActivity extends AuthenticatedActivity | |||
|         @Override | ||||
|         public void onServiceDisconnected(ComponentName componentName) { | ||||
|             // this should never happen | ||||
|             throw new RuntimeException("UploadService died but the rest of the process did not!"); | ||||
|             Timber.e(new RuntimeException("UploadService died but the rest of the process did not!")); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|  | @ -101,12 +96,8 @@ public class ContributionsActivity extends AuthenticatedActivity | |||
|     @Override | ||||
|     protected void onResume() { | ||||
|         super.onResume(); | ||||
|         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); | ||||
|         boolean isSettingsChanged = | ||||
|                 sharedPreferences.getBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false); | ||||
|         SharedPreferences.Editor editor = sharedPreferences.edit(); | ||||
|         editor.putBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false); | ||||
|         editor.apply(); | ||||
|         boolean isSettingsChanged = prefs.getBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false); | ||||
|         prefs.edit().putBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false).apply(); | ||||
|         if (isSettingsChanged) { | ||||
|             refreshSource(); | ||||
|         } | ||||
|  | @ -114,16 +105,14 @@ public class ContributionsActivity extends AuthenticatedActivity | |||
| 
 | ||||
|     @Override | ||||
|     protected void onAuthCookieAcquired(String authCookie) { | ||||
|         // Do a sync every time we get here! | ||||
|         CommonsApplication app = ((CommonsApplication) getApplication()); | ||||
|         requestSync(app.getCurrentAccount(), AUTHORITY, new Bundle()); | ||||
|         // Do a sync everytime we get here! | ||||
|         requestSync(sessionManager.getCurrentAccount(), ContributionsContentProvider.CONTRIBUTION_AUTHORITY, new Bundle()); | ||||
|         Intent uploadServiceIntent = new Intent(this, UploadService.class); | ||||
|         uploadServiceIntent.setAction(UploadService.ACTION_START_SERVICE); | ||||
|         startService(uploadServiceIntent); | ||||
|         bindService(uploadServiceIntent, uploadServiceConnection, Context.BIND_AUTO_CREATE); | ||||
| 
 | ||||
|         allContributions = getContentResolver().query(BASE_URI, ALL_FIELDS, | ||||
|                 CONTRIBUTION_SELECTION, null, CONTRIBUTION_SORT); | ||||
|         allContributions = contributionDao.loadAllContributions(); | ||||
| 
 | ||||
|         getSupportLoaderManager().initLoader(0, null, this); | ||||
|     } | ||||
|  | @ -137,17 +126,20 @@ public class ContributionsActivity extends AuthenticatedActivity | |||
|         // Activity can call methods in the fragment by acquiring a | ||||
|         // reference to the Fragment from FragmentManager, using findFragmentById() | ||||
|         FragmentManager supportFragmentManager = getSupportFragmentManager(); | ||||
|         contributionsList = (ContributionsListFragment) supportFragmentManager | ||||
|         contributionsList = (ContributionsListFragment)supportFragmentManager | ||||
|                 .findFragmentById(R.id.contributionsListFragment); | ||||
| 
 | ||||
|         supportFragmentManager.addOnBackStackChangedListener(this); | ||||
|         if (savedInstanceState != null) { | ||||
|             mediaDetails = (MediaDetailPagerFragment) supportFragmentManager | ||||
|             mediaDetails = (MediaDetailPagerFragment)supportFragmentManager | ||||
|                     .findFragmentById(R.id.contributionsFragmentContainer); | ||||
| 
 | ||||
|             getSupportLoaderManager().initLoader(0, null, this); | ||||
|         } | ||||
|         requestAuthToken(); | ||||
|         initDrawer(); | ||||
|         setTitle(getString(R.string.title_activity_contributions)); | ||||
|         setUploadCount(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|  | @ -178,24 +170,23 @@ public class ContributionsActivity extends AuthenticatedActivity | |||
| 
 | ||||
|     public void retryUpload(int i) { | ||||
|         allContributions.moveToPosition(i); | ||||
|         Contribution c = Contribution.fromCursor(allContributions); | ||||
|         Contribution c = contributionDao.fromCursor(allContributions); | ||||
|         if (c.getState() == STATE_FAILED) { | ||||
|             uploadService.queue(UploadService.ACTION_UPLOAD_FILE, c); | ||||
|             Timber.d("Restarting for %s", c.toContentValues()); | ||||
|             Timber.d("Restarting for %s", c.toString()); | ||||
|         } else { | ||||
|             Timber.d("Skipping re-upload for non-failed %s", c.toContentValues()); | ||||
|             Timber.d("Skipping re-upload for non-failed %s", c.toString()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public void deleteUpload(int i) { | ||||
|         allContributions.moveToPosition(i); | ||||
|         Contribution c = Contribution.fromCursor(allContributions); | ||||
|         Contribution c = contributionDao.fromCursor(allContributions); | ||||
|         if (c.getState() == STATE_FAILED) { | ||||
|             Timber.d("Deleting failed contrib %s", c.toContentValues()); | ||||
|             c.setContentProviderClient(getContentResolver().acquireContentProviderClient(AUTHORITY)); | ||||
|             c.delete(); | ||||
|             Timber.d("Deleting failed contrib %s", c.toString()); | ||||
|             contributionDao.delete(c); | ||||
|         } else { | ||||
|             Timber.d("Skipping deletion for non-failed contrib %s", c.toContentValues()); | ||||
|             Timber.d("Skipping deletion for non-failed contrib %s", c.toString()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -229,24 +220,23 @@ public class ContributionsActivity extends AuthenticatedActivity | |||
| 
 | ||||
|     @Override | ||||
|     public Loader<Cursor> onCreateLoader(int i, Bundle bundle) { | ||||
|         SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); | ||||
|         int uploads = sharedPref.getInt(UPLOADS_SHOWING, 100); | ||||
|         int uploads = prefs.getInt(UPLOADS_SHOWING, 100); | ||||
|         return new CursorLoader(this, BASE_URI, | ||||
|                 ALL_FIELDS, CONTRIBUTION_SELECTION, null, | ||||
|                 CONTRIBUTION_SORT + "LIMIT " + uploads); | ||||
|                 ALL_FIELDS, "", null, | ||||
|                 ContributionDao.CONTRIBUTION_SORT + "LIMIT " + uploads); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) { | ||||
|         contributionsList.changeProgressBarVisibility(false); | ||||
| 
 | ||||
|         if (contributionsList.getAdapter() == null) { | ||||
|             contributionsList.setAdapter(new ContributionsListAdapter(getApplicationContext(), | ||||
|                     cursor, 0)); | ||||
|                     cursor, 0, contributionDao)); | ||||
|         } else { | ||||
|             ((CursorAdapter) contributionsList.getAdapter()).swapCursor(cursor); | ||||
|         } | ||||
| 
 | ||||
|         setUploadCount(); | ||||
| 
 | ||||
|         contributionsList.clearSyncMessage(); | ||||
|         notifyAndMigrateDataSetObservers(); | ||||
|     } | ||||
|  | @ -263,7 +253,7 @@ public class ContributionsActivity extends AuthenticatedActivity | |||
|             // not yet ready to return data | ||||
|             return null; | ||||
|         } else { | ||||
|             return Contribution.fromCursor((Cursor) contributionsList.getAdapter().getItem(i)); | ||||
|             return contributionDao.fromCursor((Cursor) contributionsList.getAdapter().getItem(i)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -277,19 +267,16 @@ public class ContributionsActivity extends AuthenticatedActivity | |||
| 
 | ||||
|     @SuppressWarnings("ConstantConditions") | ||||
|     private void setUploadCount() { | ||||
|         CommonsApplication app = ((CommonsApplication) getApplication()); | ||||
|         compositeDisposable.add( | ||||
|                 app.getMWApi() | ||||
|                         .getUploadCount(app.getCurrentAccount().name) | ||||
|                         .subscribeOn(Schedulers.io()) | ||||
|                         .observeOn(AndroidSchedulers.mainThread()) | ||||
|                         .subscribe( | ||||
|                                 uploadCount -> getSupportActionBar().setSubtitle(getResources() | ||||
|                                         .getQuantityString(R.plurals.contributions_subtitle, | ||||
|                                                 uploadCount, uploadCount)), | ||||
|                                 t -> Timber.e(t, "Fetching upload count failed") | ||||
|                         ) | ||||
|         ); | ||||
|         compositeDisposable.add(mediaWikiApi | ||||
|                 .getUploadCount(sessionManager.getCurrentAccount().name) | ||||
|                 .subscribeOn(Schedulers.io()) | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .subscribe( | ||||
|                         uploadCount -> getSupportActionBar().setSubtitle(getResources() | ||||
|                                 .getQuantityString(R.plurals.contributions_subtitle, | ||||
|                                         uploadCount, uploadCount)), | ||||
|                         t -> Timber.e(t, "Fetching upload count failed") | ||||
|                 )); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|  | @ -341,9 +328,4 @@ public class ContributionsActivity extends AuthenticatedActivity | |||
|     public void refreshSource() { | ||||
|         getSupportLoaderManager().restartLoader(0, null, this); | ||||
|     } | ||||
| 
 | ||||
|     public static void startYourself(Context context) { | ||||
|         context.startActivity(new Intent(context, ContributionsActivity.class)); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| package fr.free.nrw.commons.contributions; | ||||
| 
 | ||||
| import android.content.ContentProvider; | ||||
| import android.content.ContentValues; | ||||
| import android.content.UriMatcher; | ||||
| import android.database.Cursor; | ||||
|  | @ -10,36 +9,36 @@ import android.net.Uri; | |||
| import android.support.annotation.NonNull; | ||||
| import android.text.TextUtils; | ||||
| 
 | ||||
| import fr.free.nrw.commons.CommonsApplication; | ||||
| import javax.inject.Inject; | ||||
| 
 | ||||
| import fr.free.nrw.commons.data.DBOpenHelper; | ||||
| import fr.free.nrw.commons.di.CommonsDaggerContentProvider; | ||||
| import timber.log.Timber; | ||||
| 
 | ||||
| import static android.content.UriMatcher.NO_MATCH; | ||||
| import static fr.free.nrw.commons.contributions.Contribution.Table.ALL_FIELDS; | ||||
| import static fr.free.nrw.commons.contributions.Contribution.Table.TABLE_NAME; | ||||
| import static fr.free.nrw.commons.contributions.ContributionDao.Table.ALL_FIELDS; | ||||
| import static fr.free.nrw.commons.contributions.ContributionDao.Table.TABLE_NAME; | ||||
| 
 | ||||
| public class ContributionsContentProvider extends ContentProvider { | ||||
| public class ContributionsContentProvider extends CommonsDaggerContentProvider { | ||||
| 
 | ||||
|     private static final int CONTRIBUTIONS = 1; | ||||
|     private static final int CONTRIBUTIONS_ID = 2; | ||||
|     private static final String BASE_PATH = "contributions"; | ||||
|     private static final UriMatcher uriMatcher = new UriMatcher(NO_MATCH); | ||||
|     public static final String AUTHORITY = "fr.free.nrw.commons.contributions.contentprovider"; | ||||
|     public static final String CONTRIBUTION_AUTHORITY = "fr.free.nrw.commons.contributions.contentprovider"; | ||||
| 
 | ||||
|     public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH); | ||||
|     public static final Uri BASE_URI = Uri.parse("content://" + CONTRIBUTION_AUTHORITY + "/" + BASE_PATH); | ||||
| 
 | ||||
|     static { | ||||
|         uriMatcher.addURI(AUTHORITY, BASE_PATH, CONTRIBUTIONS); | ||||
|         uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", CONTRIBUTIONS_ID); | ||||
|         uriMatcher.addURI(CONTRIBUTION_AUTHORITY, BASE_PATH, CONTRIBUTIONS); | ||||
|         uriMatcher.addURI(CONTRIBUTION_AUTHORITY, BASE_PATH + "/#", CONTRIBUTIONS_ID); | ||||
|     } | ||||
| 
 | ||||
|     public static Uri uriForId(int id) { | ||||
|         return Uri.parse(BASE_URI.toString() + "/" + id); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean onCreate() { | ||||
|         return false; | ||||
|     } | ||||
|     @Inject DBOpenHelper dbOpenHelper; | ||||
| 
 | ||||
|     @SuppressWarnings("ConstantConditions") | ||||
|     @Override | ||||
|  | @ -50,8 +49,7 @@ public class ContributionsContentProvider extends ContentProvider { | |||
| 
 | ||||
|         int uriType = uriMatcher.match(uri); | ||||
| 
 | ||||
|         CommonsApplication app = (CommonsApplication) getContext().getApplicationContext(); | ||||
|         SQLiteDatabase db = app.getDBOpenHelper().getReadableDatabase(); | ||||
|         SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); | ||||
|         Cursor cursor; | ||||
| 
 | ||||
|         switch (uriType) { | ||||
|  | @ -87,8 +85,7 @@ public class ContributionsContentProvider extends ContentProvider { | |||
|     @Override | ||||
|     public Uri insert(@NonNull Uri uri, ContentValues contentValues) { | ||||
|         int uriType = uriMatcher.match(uri); | ||||
|         CommonsApplication app = (CommonsApplication) getContext().getApplicationContext(); | ||||
|         SQLiteDatabase sqlDB = app.getDBOpenHelper().getWritableDatabase(); | ||||
|         SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); | ||||
|         long id; | ||||
|         switch (uriType) { | ||||
|             case CONTRIBUTIONS: | ||||
|  | @ -107,13 +104,12 @@ public class ContributionsContentProvider extends ContentProvider { | |||
|         int rows; | ||||
|         int uriType = uriMatcher.match(uri); | ||||
| 
 | ||||
|         CommonsApplication app = (CommonsApplication) getContext().getApplicationContext(); | ||||
|         SQLiteDatabase sqlDB = app.getDBOpenHelper().getWritableDatabase(); | ||||
|         SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); | ||||
| 
 | ||||
|         switch (uriType) { | ||||
|             case CONTRIBUTIONS_ID: | ||||
|                 Timber.d("Deleting contribution id %s", uri.getLastPathSegment()); | ||||
|                 rows = sqlDB.delete(TABLE_NAME, | ||||
|                 rows = db.delete(TABLE_NAME, | ||||
|                         "_id = ?", | ||||
|                         new String[]{uri.getLastPathSegment()} | ||||
|                 ); | ||||
|  | @ -130,8 +126,7 @@ public class ContributionsContentProvider extends ContentProvider { | |||
|     public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) { | ||||
|         Timber.d("Hello, bulk insert! (ContributionsContentProvider)"); | ||||
|         int uriType = uriMatcher.match(uri); | ||||
|         CommonsApplication app = (CommonsApplication) getContext().getApplicationContext(); | ||||
|         SQLiteDatabase sqlDB = app.getDBOpenHelper().getWritableDatabase(); | ||||
|         SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); | ||||
|         sqlDB.beginTransaction(); | ||||
|         switch (uriType) { | ||||
|             case CONTRIBUTIONS: | ||||
|  | @ -162,8 +157,7 @@ public class ContributionsContentProvider extends ContentProvider { | |||
|         error out otherwise. | ||||
|          */ | ||||
|         int uriType = uriMatcher.match(uri); | ||||
|         CommonsApplication app = (CommonsApplication) getContext().getApplicationContext(); | ||||
|         SQLiteDatabase sqlDB = app.getDBOpenHelper().getWritableDatabase(); | ||||
|         SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); | ||||
|         int rowsUpdated; | ||||
|         switch (uriType) { | ||||
|             case CONTRIBUTIONS: | ||||
|  | @ -175,7 +169,7 @@ public class ContributionsContentProvider extends ContentProvider { | |||
|                 if (TextUtils.isEmpty(selection)) { | ||||
|                     rowsUpdated = sqlDB.update(TABLE_NAME, | ||||
|                             contentValues, | ||||
|                             Contribution.Table.COLUMN_ID + " = ?", | ||||
|                             ContributionDao.Table.COLUMN_ID + " = ?", | ||||
|                             new String[]{String.valueOf(id)}); | ||||
|                 } else { | ||||
|                     throw new IllegalArgumentException( | ||||
|  |  | |||
|  | @ -11,8 +11,11 @@ import fr.free.nrw.commons.R; | |||
| 
 | ||||
| class ContributionsListAdapter extends CursorAdapter { | ||||
| 
 | ||||
|     public ContributionsListAdapter(Context context, Cursor c, int flags) { | ||||
|     private final ContributionDao contributionDao; | ||||
| 
 | ||||
|     public ContributionsListAdapter(Context context, Cursor c, int flags, ContributionDao contributionDao) { | ||||
|         super(context, c, flags); | ||||
|         this.contributionDao = contributionDao; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|  | @ -26,7 +29,7 @@ class ContributionsListAdapter extends CursorAdapter { | |||
|     @Override | ||||
|     public void bindView(View view, Context context, Cursor cursor) { | ||||
|         final ContributionViewHolder views = (ContributionViewHolder)view.getTag(); | ||||
|         final Contribution contribution = Contribution.fromCursor(cursor); | ||||
|         final Contribution contribution = contributionDao.fromCursor(cursor); | ||||
| 
 | ||||
|         views.imageView.setMedia(contribution); | ||||
|         views.titleView.setText(contribution.getDisplayTitle()); | ||||
|  | @ -34,7 +37,7 @@ class ContributionsListAdapter extends CursorAdapter { | |||
|         views.seqNumView.setText(String.valueOf(cursor.getPosition() + 1)); | ||||
|         views.seqNumView.setVisibility(View.VISIBLE); | ||||
| 
 | ||||
|         switch(contribution.getState()) { | ||||
|         switch (contribution.getState()) { | ||||
|             case Contribution.STATE_COMPLETED: | ||||
|                 views.stateView.setVisibility(View.GONE); | ||||
|                 views.progressView.setVisibility(View.GONE); | ||||
|  | @ -50,7 +53,7 @@ class ContributionsListAdapter extends CursorAdapter { | |||
|                 views.progressView.setVisibility(View.VISIBLE); | ||||
|                 long total = contribution.getDataLength(); | ||||
|                 long transferred = contribution.getTransferred(); | ||||
|                 if(transferred == 0 || transferred >= total) { | ||||
|                 if (transferred == 0 || transferred >= total) { | ||||
|                     views.progressView.setIndeterminate(true); | ||||
|                 } else { | ||||
|                     views.progressView.setProgress((int)(((double)transferred / (double)total) * 100)); | ||||
|  |  | |||
|  | @ -5,9 +5,7 @@ import android.content.SharedPreferences; | |||
| import android.content.pm.PackageManager; | ||||
| import android.os.Build; | ||||
| import android.os.Bundle; | ||||
| import android.preference.PreferenceManager; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.v4.app.Fragment; | ||||
| import android.support.v4.content.ContextCompat; | ||||
| import android.support.v7.app.AlertDialog; | ||||
| import android.view.LayoutInflater; | ||||
|  | @ -19,30 +17,43 @@ import android.view.ViewGroup; | |||
| import android.widget.AdapterView; | ||||
| import android.widget.GridView; | ||||
| import android.widget.ListAdapter; | ||||
| import android.widget.ProgressBar; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import java.util.Arrays; | ||||
| 
 | ||||
| import javax.inject.Inject; | ||||
| import javax.inject.Named; | ||||
| 
 | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
| import fr.free.nrw.commons.CommonsApplication; | ||||
| import fr.free.nrw.commons.R; | ||||
| import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; | ||||
| import fr.free.nrw.commons.nearby.NearbyActivity; | ||||
| import timber.log.Timber; | ||||
| 
 | ||||
| import static android.Manifest.permission.READ_EXTERNAL_STORAGE; | ||||
| import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; | ||||
| import static android.app.Activity.RESULT_OK; | ||||
| import static android.content.Context.MODE_PRIVATE; | ||||
| import static android.content.pm.PackageManager.PERMISSION_GRANTED; | ||||
| import static android.view.View.GONE; | ||||
| 
 | ||||
| public class ContributionsListFragment extends Fragment { | ||||
| public class ContributionsListFragment extends CommonsDaggerSupportFragment { | ||||
| 
 | ||||
|     @BindView(R.id.contributionsList) | ||||
|     GridView contributionsList; | ||||
|     @BindView(R.id.waitingMessage) | ||||
|     TextView waitingMessage; | ||||
|     @BindView(R.id.emptyMessage) | ||||
|     TextView emptyMessage; | ||||
|     @BindView(R.id.loadingContributionsProgressBar) | ||||
|     ProgressBar progressBar; | ||||
| 
 | ||||
|     @Inject | ||||
|     @Named("prefs") | ||||
|     SharedPreferences prefs; | ||||
|     @Inject | ||||
|     @Named("default_preferences") | ||||
|     SharedPreferences defaultPrefs; | ||||
| 
 | ||||
|     private ContributionController controller; | ||||
| 
 | ||||
|     @Override | ||||
|  | @ -57,7 +68,6 @@ public class ContributionsListFragment extends Fragment { | |||
|         } | ||||
| 
 | ||||
|         //TODO: Should this be in onResume? | ||||
|         SharedPreferences prefs = getActivity().getSharedPreferences("prefs", MODE_PRIVATE); | ||||
|         String lastModified = prefs.getString("lastSyncTimestamp", ""); | ||||
|         Timber.d("Last Sync Timestamp: %s", lastModified); | ||||
| 
 | ||||
|  | @ -67,6 +77,7 @@ public class ContributionsListFragment extends Fragment { | |||
|             waitingMessage.setVisibility(GONE); | ||||
|         } | ||||
| 
 | ||||
|         changeProgressBarVisibility(true); | ||||
|         return v; | ||||
|     } | ||||
| 
 | ||||
|  | @ -78,6 +89,10 @@ public class ContributionsListFragment extends Fragment { | |||
|         this.contributionsList.setAdapter(adapter); | ||||
|     } | ||||
| 
 | ||||
|     public void changeProgressBarVisibility(boolean isVisible) { | ||||
|         this.progressBar.setVisibility(isVisible ? View.VISIBLE : View.GONE); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onSaveInstanceState(Bundle outState) { | ||||
|         if (outState == null) { | ||||
|  | @ -155,9 +170,7 @@ public class ContributionsListFragment extends Fragment { | |||
| 
 | ||||
|                 return true; | ||||
|             case R.id.menu_from_camera: | ||||
|                 SharedPreferences sharedPref = PreferenceManager | ||||
|                         .getDefaultSharedPreferences(CommonsApplication.getInstance()); | ||||
|                 boolean useExtStorage = sharedPref.getBoolean("useExternalStorage", true); | ||||
|                 boolean useExtStorage = defaultPrefs.getBoolean("useExternalStorage", true); | ||||
|                 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && useExtStorage) { | ||||
|                     // Here, thisActivity is the current activity | ||||
|                     if (ContextCompat.checkSelfPermission(getActivity(), WRITE_EXTERNAL_STORAGE) | ||||
|  | @ -201,7 +214,7 @@ public class ContributionsListFragment extends Fragment { | |||
|     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, | ||||
|                                            @NonNull int[] grantResults) { | ||||
|         Timber.d("onRequestPermissionsResult: req code = " + " perm = " | ||||
|                 + permissions + " grant =" + grantResults); | ||||
|                 + Arrays.toString(permissions) + " grant =" + Arrays.toString(grantResults)); | ||||
| 
 | ||||
|         switch (requestCode) { | ||||
|             // 1 = Storage allowed when gallery selected | ||||
|  | @ -235,12 +248,17 @@ public class ContributionsListFragment extends Fragment { | |||
|         menu.clear(); // See http://stackoverflow.com/a/8495697/17865 | ||||
|         inflater.inflate(R.menu.fragment_contributions_list, menu); | ||||
| 
 | ||||
|         CommonsApplication app = (CommonsApplication) getContext().getApplicationContext(); | ||||
|         if (!app.deviceHasCamera()) { | ||||
|         if (!deviceHasCamera()) { | ||||
|             menu.findItem(R.id.menu_from_camera).setEnabled(false); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public boolean deviceHasCamera() { | ||||
|         PackageManager pm = getContext().getPackageManager(); | ||||
|         return pm.hasSystemFeature(PackageManager.FEATURE_CAMERA) || | ||||
|                 pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onCreate(Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|  |  | |||
|  | @ -13,21 +13,27 @@ import android.os.RemoteException; | |||
| import android.text.TextUtils; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.text.SimpleDateFormat; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
| import java.util.Locale; | ||||
| import java.util.TimeZone; | ||||
| 
 | ||||
| import javax.inject.Inject; | ||||
| import javax.inject.Named; | ||||
| 
 | ||||
| import fr.free.nrw.commons.CommonsApplication; | ||||
| import fr.free.nrw.commons.Utils; | ||||
| import fr.free.nrw.commons.di.ApplicationlessInjection; | ||||
| import fr.free.nrw.commons.mwapi.LogEventResult; | ||||
| import fr.free.nrw.commons.mwapi.MediaWikiApi; | ||||
| import timber.log.Timber; | ||||
| 
 | ||||
| import static android.content.Context.MODE_PRIVATE; | ||||
| import static fr.free.nrw.commons.contributions.Contribution.STATE_COMPLETED; | ||||
| import static fr.free.nrw.commons.contributions.Contribution.Table.COLUMN_FILENAME; | ||||
| import static fr.free.nrw.commons.contributions.ContributionDao.Table.COLUMN_FILENAME; | ||||
| import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI; | ||||
| 
 | ||||
| @SuppressWarnings("WeakerAccess") | ||||
| public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter { | ||||
| 
 | ||||
|     private static final String[] existsQuery = {COLUMN_FILENAME}; | ||||
|  | @ -35,6 +41,10 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter { | |||
|     private static final ContentValues[] EMPTY = {}; | ||||
|     private static int COMMIT_THRESHOLD = 10; | ||||
| 
 | ||||
|     @SuppressWarnings("WeakerAccess") | ||||
|     @Inject MediaWikiApi mwApi; | ||||
|     @Inject @Named("prefs") SharedPreferences prefs; | ||||
| 
 | ||||
|     public ContributionsSyncAdapter(Context context, boolean autoInitialize) { | ||||
|         super(context, autoInitialize); | ||||
|     } | ||||
|  | @ -47,6 +57,9 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter { | |||
|     } | ||||
| 
 | ||||
|     private boolean fileExists(ContentProviderClient client, String filename) { | ||||
|         if (filename == null) { | ||||
|             return false; | ||||
|         } | ||||
|         Cursor cursor = null; | ||||
|         try { | ||||
|             cursor = client.query(BASE_URI, | ||||
|  | @ -68,19 +81,23 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter { | |||
|     @Override | ||||
|     public void onPerformSync(Account account, Bundle bundle, String authority, | ||||
|                               ContentProviderClient contentProviderClient, SyncResult syncResult) { | ||||
|         ApplicationlessInjection | ||||
|                 .getInstance(getContext() | ||||
|                         .getApplicationContext()) | ||||
|                 .getCommonsApplicationComponent() | ||||
|                 .inject(this); | ||||
|         // This code is fraught with possibilities of race conditions, but lalalalala I can't hear you! | ||||
|         String user = account.name; | ||||
|         MediaWikiApi api = CommonsApplication.getInstance().getMWApi(); | ||||
|         SharedPreferences prefs = getContext().getSharedPreferences("prefs", MODE_PRIVATE); | ||||
|         String lastModified = prefs.getString("lastSyncTimestamp", ""); | ||||
|         Date curTime = new Date(); | ||||
|         LogEventResult result; | ||||
|         Boolean done = false; | ||||
|         String queryContinue = null; | ||||
|         ContributionDao contributionDao = new ContributionDao(() -> contentProviderClient); | ||||
|         while (!done) { | ||||
| 
 | ||||
|             try { | ||||
|                 result = api.logEvents(user, lastModified, queryContinue, getLimit()); | ||||
|                 result = mwApi.logEvents(user, lastModified, queryContinue, getLimit()); | ||||
|             } catch (IOException e) { | ||||
|                 // There isn't really much we can do, eh? | ||||
|                 // FIXME: Perhaps add EventLogging? | ||||
|  | @ -109,7 +126,7 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter { | |||
|                         "", -1, dateUpdated, dateUpdated, user, | ||||
|                         "", ""); | ||||
|                 contrib.setState(STATE_COMPLETED); | ||||
|                 imageValues.add(contrib.toContentValues()); | ||||
|                 imageValues.add(contributionDao.toContentValues(contrib)); | ||||
| 
 | ||||
|                 if (imageValues.size() % COMMIT_THRESHOLD == 0) { | ||||
|                     try { | ||||
|  | @ -134,8 +151,13 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter { | |||
|                 done = true; | ||||
|             } | ||||
|         } | ||||
|         prefs.edit().putString("lastSyncTimestamp", Utils.toMWDate(curTime)).apply(); | ||||
|         prefs.edit().putString("lastSyncTimestamp", toMWDate(curTime)).apply(); | ||||
|         Timber.d("Oh hai, everyone! Look, a kitty!"); | ||||
|     } | ||||
| 
 | ||||
|     private String toMWDate(Date date) { | ||||
|         SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH); // Assuming MW always gives me UTC | ||||
|         isoFormat.setTimeZone(TimeZone.getTimeZone("UTC")); | ||||
|         return isoFormat.format(date); | ||||
|     } | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vishan Seru
						Vishan Seru