diff --git a/build.gradle b/build.gradle index 7fbcbe593..97a26f4be 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { classpath 'com.github.ben-manes:gradle-versions-plugin:0.13.0' - classpath 'com.android.tools.build:gradle:2.2.2' + classpath 'com.android.tools.build:gradle:2.2.3' classpath 'com.google.gms:google-services:3.0.0' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' classpath('fr.avianey.androidsvgdrawable:gradle-plugin:3.0.0') { diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/annotation/CustomTabType.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/annotation/CustomTabType.java index 825f573dc..ce6e148ff 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/annotation/CustomTabType.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/annotation/CustomTabType.java @@ -2,6 +2,9 @@ package org.mariotaku.twidere.annotation; import android.support.annotation.StringDef; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Created by mariotaku on 16/1/28. */ @@ -15,6 +18,7 @@ import android.support.annotation.StringDef; CustomTabType.SEARCH_STATUSES, CustomTabType.LIST_TIMELINE, }) +@Retention(RetentionPolicy.SOURCE) public @interface CustomTabType { String HOME_TIMELINE = "home_timeline"; String NOTIFICATIONS_TIMELINE = "notifications_timeline"; diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/Draft.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/Draft.java index 0993beebd..3c324e9ca 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/Draft.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/Draft.java @@ -30,7 +30,7 @@ import com.hannesdorfmann.parcelableplease.annotation.ParcelableThisPlease; import org.mariotaku.commons.objectcursor.LoganSquareCursorFieldConverter; import org.mariotaku.library.objectcursor.annotation.CursorField; import org.mariotaku.library.objectcursor.annotation.CursorObject; -import org.mariotaku.twidere.model.draft.ActionExtra; +import org.mariotaku.twidere.model.draft.ActionExtras; import org.mariotaku.twidere.model.util.DraftExtrasConverter; import org.mariotaku.twidere.model.util.UserKeysCursorFieldConverter; import org.mariotaku.twidere.provider.TwidereDataStore; @@ -69,7 +69,7 @@ public class Draft implements Parcelable { @Nullable @ParcelableThisPlease @CursorField(value = Drafts.ACTION_EXTRAS, converter = DraftExtrasConverter.class) - public ActionExtra action_extras; + public ActionExtras action_extras; public Draft() { diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ParcelableLocation.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ParcelableLocation.java index fa02f15ee..1419df757 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ParcelableLocation.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ParcelableLocation.java @@ -23,6 +23,7 @@ import android.content.ContentValues; import android.database.Cursor; import android.os.Parcel; import android.os.Parcelable; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.bluelinelabs.logansquare.annotation.JsonField; @@ -85,7 +86,7 @@ public class ParcelableLocation implements Parcelable { } @Nullable - public static ParcelableLocation valueOf(@Nullable final String locationString) { + public static ParcelableLocation valueOf(@NonNull final String locationString) { if (locationString == null) return null; final String[] longlat = locationString.split(","); if (longlat.length != 2) { @@ -106,7 +107,9 @@ public class ParcelableLocation implements Parcelable { public static class Converter implements CursorFieldConverter { @Override public ParcelableLocation parseField(Cursor cursor, int columnIndex, ParameterizedType fieldType) { - return valueOf(cursor.getString(columnIndex)); + final String locationString = cursor.getString(columnIndex); + if (locationString == null) return null; + return valueOf(locationString); } @Override diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/draft/ActionExtra.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/draft/ActionExtras.java similarity index 70% rename from twidere.component.common/src/main/java/org/mariotaku/twidere/model/draft/ActionExtra.java rename to twidere.component.common/src/main/java/org/mariotaku/twidere/model/draft/ActionExtras.java index 7d42f8eb9..7a92ddfd3 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/draft/ActionExtra.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/draft/ActionExtras.java @@ -5,5 +5,5 @@ import android.os.Parcelable; /** * Created by mariotaku on 16/2/21. */ -public interface ActionExtra extends Parcelable { +public interface ActionExtras extends Parcelable { } diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/draft/SendDirectMessageActionExtra.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/draft/SendDirectMessageActionExtra.java deleted file mode 100644 index 62d34f502..000000000 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/draft/SendDirectMessageActionExtra.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.mariotaku.twidere.model.draft; - -import android.os.Parcel; - -import com.bluelinelabs.logansquare.annotation.JsonField; -import com.bluelinelabs.logansquare.annotation.JsonObject; -import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease; -import com.hannesdorfmann.parcelableplease.annotation.ParcelableThisPlease; - -/** - * Created by mariotaku on 16/2/21. - */ -@ParcelablePlease -@JsonObject -public class SendDirectMessageActionExtra implements ActionExtra { - @ParcelableThisPlease - @JsonField(name = "recipient_id") - String recipientId; - - public String getRecipientId() { - return recipientId; - } - - public void setRecipientId(String recipientId) { - this.recipientId = recipientId; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - SendDirectMessageActionExtraParcelablePlease.writeToParcel(this, dest, flags); - } - - public static final Creator CREATOR = new Creator() { - @Override - public SendDirectMessageActionExtra createFromParcel(Parcel source) { - SendDirectMessageActionExtra target = new SendDirectMessageActionExtra(); - SendDirectMessageActionExtraParcelablePlease.readFromParcel(target, source); - return target; - } - - @Override - public SendDirectMessageActionExtra[] newArray(int size) { - return new SendDirectMessageActionExtra[size]; - } - }; -} diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/draft/SendDirectMessageActionExtras.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/draft/SendDirectMessageActionExtras.java new file mode 100644 index 000000000..1191d5025 --- /dev/null +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/draft/SendDirectMessageActionExtras.java @@ -0,0 +1,67 @@ +package org.mariotaku.twidere.model.draft; + +import android.os.Parcel; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; +import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease; +import com.hannesdorfmann.parcelableplease.annotation.ParcelableThisPlease; + +/** + * Created by mariotaku on 16/2/21. + */ +@ParcelablePlease +@JsonObject +public class SendDirectMessageActionExtras implements ActionExtras { + @ParcelableThisPlease + @JsonField(name = "recipient_id") + String recipientId; + + public String getRecipientId() { + return recipientId; + } + + public void setRecipientId(String recipientId) { + this.recipientId = recipientId; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + SendDirectMessageActionExtrasParcelablePlease.writeToParcel(this, dest, flags); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SendDirectMessageActionExtras that = (SendDirectMessageActionExtras) o; + + return recipientId != null ? recipientId.equals(that.recipientId) : that.recipientId == null; + + } + + @Override + public int hashCode() { + return recipientId != null ? recipientId.hashCode() : 0; + } + + public static final Creator CREATOR = new Creator() { + @Override + public SendDirectMessageActionExtras createFromParcel(Parcel source) { + SendDirectMessageActionExtras target = new SendDirectMessageActionExtras(); + SendDirectMessageActionExtrasParcelablePlease.readFromParcel(target, source); + return target; + } + + @Override + public SendDirectMessageActionExtras[] newArray(int size) { + return new SendDirectMessageActionExtras[size]; + } + }; +} diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/draft/UpdateStatusActionExtra.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/draft/UpdateStatusActionExtras.java similarity index 54% rename from twidere.component.common/src/main/java/org/mariotaku/twidere/model/draft/UpdateStatusActionExtra.java rename to twidere.component.common/src/main/java/org/mariotaku/twidere/model/draft/UpdateStatusActionExtras.java index 22fee9135..32f9c75ee 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/draft/UpdateStatusActionExtra.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/draft/UpdateStatusActionExtras.java @@ -14,7 +14,7 @@ import org.mariotaku.twidere.model.ParcelableStatus; */ @ParcelablePlease @JsonObject -public class UpdateStatusActionExtra implements ActionExtra { +public class UpdateStatusActionExtras implements ActionExtras { @ParcelableThisPlease @JsonField(name = "in_reply_to_status") ParcelableStatus inReplyToStatus; @@ -78,20 +78,47 @@ public class UpdateStatusActionExtra implements ActionExtra { @Override public void writeToParcel(Parcel dest, int flags) { - UpdateStatusActionExtraParcelablePlease.writeToParcel(this, dest, flags); + UpdateStatusActionExtrasParcelablePlease.writeToParcel(this, dest, flags); } - public static final Creator CREATOR = new Creator() { + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + UpdateStatusActionExtras that = (UpdateStatusActionExtras) o; + + if (possiblySensitive != that.possiblySensitive) return false; + if (displayCoordinates != that.displayCoordinates) return false; + if (inReplyToStatus != null ? !inReplyToStatus.equals(that.inReplyToStatus) : that.inReplyToStatus != null) + return false; + if (repostStatusId != null ? !repostStatusId.equals(that.repostStatusId) : that.repostStatusId != null) + return false; + return attachmentUrl != null ? attachmentUrl.equals(that.attachmentUrl) : that.attachmentUrl == null; + + } + + @Override + public int hashCode() { + int result = inReplyToStatus != null ? inReplyToStatus.hashCode() : 0; + result = 31 * result + (possiblySensitive ? 1 : 0); + result = 31 * result + (repostStatusId != null ? repostStatusId.hashCode() : 0); + result = 31 * result + (displayCoordinates ? 1 : 0); + result = 31 * result + (attachmentUrl != null ? attachmentUrl.hashCode() : 0); + return result; + } + + public static final Creator CREATOR = new Creator() { @Override - public UpdateStatusActionExtra createFromParcel(Parcel source) { - UpdateStatusActionExtra target = new UpdateStatusActionExtra(); - UpdateStatusActionExtraParcelablePlease.readFromParcel(target, source); + public UpdateStatusActionExtras createFromParcel(Parcel source) { + UpdateStatusActionExtras target = new UpdateStatusActionExtras(); + UpdateStatusActionExtrasParcelablePlease.readFromParcel(target, source); return target; } @Override - public UpdateStatusActionExtra[] newArray(int size) { - return new UpdateStatusActionExtra[size]; + public UpdateStatusActionExtras[] newArray(int size) { + return new UpdateStatusActionExtras[size]; } }; } diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/util/DraftExtrasConverter.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/util/DraftExtrasConverter.java index e96415fdb..70708443b 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/util/DraftExtrasConverter.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/util/DraftExtrasConverter.java @@ -8,9 +8,9 @@ import com.bluelinelabs.logansquare.LoganSquare; import org.mariotaku.library.objectcursor.converter.CursorFieldConverter; import org.mariotaku.twidere.model.Draft; -import org.mariotaku.twidere.model.draft.ActionExtra; -import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtra; -import org.mariotaku.twidere.model.draft.UpdateStatusActionExtra; +import org.mariotaku.twidere.model.draft.ActionExtras; +import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtras; +import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras; import org.mariotaku.twidere.provider.TwidereDataStore.Drafts; import java.io.IOException; @@ -19,33 +19,30 @@ import java.lang.reflect.ParameterizedType; /** * Created by mariotaku on 16/2/20. */ -public class DraftExtrasConverter implements CursorFieldConverter { +public class DraftExtrasConverter implements CursorFieldConverter { @Override - public ActionExtra parseField(Cursor cursor, int columnIndex, ParameterizedType fieldType) throws IOException { + public ActionExtras parseField(Cursor cursor, int columnIndex, ParameterizedType fieldType) throws IOException { final String actionType = cursor.getString(cursor.getColumnIndex(Drafts.ACTION_TYPE)); - if (TextUtils.isEmpty(actionType)) return null; + final String json = cursor.getString(columnIndex); + if (TextUtils.isEmpty(actionType) || TextUtils.isEmpty(json)) return null; switch (actionType) { case "0": case "1": case Draft.Action.UPDATE_STATUS: case Draft.Action.REPLY: case Draft.Action.QUOTE: { - final String string = cursor.getString(columnIndex); - if (TextUtils.isEmpty(string)) return null; - return LoganSquare.parse(string, UpdateStatusActionExtra.class); + return LoganSquare.parse(json, UpdateStatusActionExtras.class); } case "2": case Draft.Action.SEND_DIRECT_MESSAGE: { - final String string = cursor.getString(columnIndex); - if (TextUtils.isEmpty(string)) return null; - return LoganSquare.parse(string, SendDirectMessageActionExtra.class); + return LoganSquare.parse(json, SendDirectMessageActionExtras.class); } } return null; } @Override - public void writeField(ContentValues values, ActionExtra object, String columnName, ParameterizedType fieldType) throws IOException { + public void writeField(ContentValues values, ActionExtras object, String columnName, ParameterizedType fieldType) throws IOException { if (object == null) return; values.put(columnName, LoganSquare.serialize(object)); } diff --git a/twidere/build.gradle b/twidere/build.gradle index 78d3fe518..c1056771f 100644 --- a/twidere/build.gradle +++ b/twidere/build.gradle @@ -125,6 +125,7 @@ dependencies { androidTestCompile "com.android.support:support-annotations:$android_support_lib_version" androidTestCompile 'com.android.support.test:runner:0.5' androidTestCompile 'com.android.support.test:rules:0.5' + androidTestCompile 'commons-io:commons-io:2.5' compile 'com.android.support:multidex:1.0.1' compile "com.android.support:support-v4:$android_support_lib_version" diff --git a/twidere/src/androidTest/java/org/mariotaku/twidere/extension/DraftExtensionsTest.kt b/twidere/src/androidTest/java/org/mariotaku/twidere/extension/DraftExtensionsTest.kt index 44135cf66..4f293a298 100644 --- a/twidere/src/androidTest/java/org/mariotaku/twidere/extension/DraftExtensionsTest.kt +++ b/twidere/src/androidTest/java/org/mariotaku/twidere/extension/DraftExtensionsTest.kt @@ -1,16 +1,15 @@ package org.mariotaku.twidere +import android.net.Uri import android.support.test.InstrumentationRegistry import android.support.test.runner.AndroidJUnit4 +import org.apache.commons.io.IOUtils import org.junit.Assert import org.junit.Test import org.junit.runner.RunWith import org.mariotaku.twidere.extension.readMimeMessageFrom import org.mariotaku.twidere.extension.writeMimeMessageTo -import org.mariotaku.twidere.model.Draft -import org.mariotaku.twidere.model.ParcelableMedia -import org.mariotaku.twidere.model.ParcelableMediaUpdate -import org.mariotaku.twidere.model.UserKey +import org.mariotaku.twidere.model.* import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.util.concurrent.TimeUnit @@ -24,16 +23,24 @@ class DraftExtensionsTest { fun testMimeMessageProcessing() { val context = InstrumentationRegistry.getTargetContext() val draft = Draft() + draft.action_type = Draft.Action.UPDATE_STATUS draft.timestamp = System.currentTimeMillis() draft.account_keys = arrayOf(UserKey("user1", "twitter.com"), UserKey("user2", "twitter.com")) - draft.text = "Hello world" - draft.media = arrayOf(ParcelableMediaUpdate().apply { - this.uri = "file:///system/media/audio/ringtones/Atria.ogg" - this.type = ParcelableMedia.Type.VIDEO - this.alt_text = String(CharArray(420).apply { - fill('A') - }) - }) + draft.text = "Hello world 测试" + draft.location = ParcelableLocation(-11.956, 99.625) // Randomly generated + draft.media = arrayOf( + "file:///system/media/audio/ringtones/Atria.ogg", + "file:///system/media/audio/ringtones/Callisto.ogg", + "file:///system/media/audio/ringtones/Dione.ogg" + ).map { uri -> + ParcelableMediaUpdate().apply { + this.uri = uri + this.type = ParcelableMedia.Type.VIDEO + this.alt_text = String(CharArray(420).apply { + fill('A') + }) + } + }.toTypedArray() val output = ByteArrayOutputStream() draft.writeMimeMessageTo(context, output) val input = ByteArrayInputStream(output.toByteArray()) @@ -44,10 +51,16 @@ class DraftExtensionsTest { Assert.assertArrayEquals(draft.account_keys?.sortedArray(), newDraft.account_keys?.sortedArray()) Assert.assertEquals(TimeUnit.MILLISECONDS.toSeconds(draft.timestamp), TimeUnit.MILLISECONDS.toSeconds(newDraft.timestamp)) Assert.assertEquals(draft.text, newDraft.text) + Assert.assertEquals(draft.location, newDraft.location) + Assert.assertEquals(draft.action_type, newDraft.action_type) + Assert.assertEquals(draft.action_extras, newDraft.action_extras) draft.media?.forEachIndexed { idx, expected -> val actual = newDraft.media!![idx] Assert.assertEquals(expected.alt_text, actual.alt_text) Assert.assertEquals(expected.type, actual.type) + val stl = context.contentResolver.openInputStream(Uri.parse(expected.uri)) + val str = context.contentResolver.openInputStream(Uri.parse(actual.uri)) + Assert.assertTrue(IOUtils.contentEquals(stl, str)) } } } \ No newline at end of file diff --git a/twidere/src/google/AndroidManifest.xml b/twidere/src/google/AndroidManifest.xml index 868423d78..450c08e39 100644 --- a/twidere/src/google/AndroidManifest.xml +++ b/twidere/src/google/AndroidManifest.xml @@ -29,6 +29,9 @@ + + + \ No newline at end of file diff --git a/twidere/src/google/kotlin/org/mariotaku/twidere/activity/DropboxAuthStarterActivity.kt b/twidere/src/google/kotlin/org/mariotaku/twidere/activity/DropboxAuthStarterActivity.kt new file mode 100644 index 000000000..92b5b3e1c --- /dev/null +++ b/twidere/src/google/kotlin/org/mariotaku/twidere/activity/DropboxAuthStarterActivity.kt @@ -0,0 +1,16 @@ +package org.mariotaku.twidere.activity + +import android.os.Bundle +import com.dropbox.core.android.Auth +import org.mariotaku.twidere.Constants.DROPBOX_APP_KEY + +/** + * Created by mariotaku on 2016/12/7. + */ +class DropboxAuthStarterActivity : BaseActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + Auth.startOAuth2Authentication(this, DROPBOX_APP_KEY) + finish() + } +} \ No newline at end of file diff --git a/twidere/src/google/kotlin/org/mariotaku/twidere/service/DropboxDataSyncService.kt b/twidere/src/google/kotlin/org/mariotaku/twidere/service/DropboxDataSyncService.kt new file mode 100644 index 000000000..e10914363 --- /dev/null +++ b/twidere/src/google/kotlin/org/mariotaku/twidere/service/DropboxDataSyncService.kt @@ -0,0 +1,56 @@ +package org.mariotaku.twidere.service + +import android.app.Service +import android.content.Intent +import android.content.SyncResult +import android.os.Bundle +import android.os.IBinder +import org.mariotaku.twidere.IDataSyncService +import org.mariotaku.twidere.activity.DropboxAuthStarterActivity +import org.mariotaku.twidere.model.SyncAuthInfo +import java.lang.ref.WeakReference + +/** + * Created by mariotaku on 2016/12/7. + */ + +class DropboxDataSyncService : Service() { + private val serviceInterface: ServiceInterface + + init { + serviceInterface = ServiceInterface(WeakReference(this)) + } + + override fun onBind(intent: Intent?): IBinder { + return serviceInterface.asBinder() + } + + private fun getAuthInfo(): SyncAuthInfo? { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + private fun getAuthRequestIntent(info: SyncAuthInfo?): Intent { + return Intent(this, DropboxAuthStarterActivity::class.java) + } + + private fun onPerformSync(info: SyncAuthInfo, extras: Bundle?, syncResult: SyncResult) { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + internal class ServiceInterface(val service: WeakReference) : IDataSyncService.Stub() { + + override fun getAuthInfo(): SyncAuthInfo? { + return service.get().getAuthInfo() + } + + override fun getAuthRequestIntent(info: SyncAuthInfo?): Intent { + return service.get().getAuthRequestIntent(info) + } + + override fun onPerformSync(info: SyncAuthInfo, extras: Bundle?, syncResult: SyncResult) { + service.get().onPerformSync(info, extras, syncResult) + } + + } + +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/model/util/ParcelableStatusUpdateUtils.java b/twidere/src/main/java/org/mariotaku/twidere/model/util/ParcelableStatusUpdateUtils.java index cf7c197d0..8c7ca5672 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/model/util/ParcelableStatusUpdateUtils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/model/util/ParcelableStatusUpdateUtils.java @@ -6,7 +6,7 @@ import android.content.Context; import org.mariotaku.twidere.model.AccountDetails; import org.mariotaku.twidere.model.Draft; import org.mariotaku.twidere.model.ParcelableStatusUpdate; -import org.mariotaku.twidere.model.draft.UpdateStatusActionExtra; +import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras; /** * Created by mariotaku on 16/2/12. @@ -25,8 +25,8 @@ public class ParcelableStatusUpdateUtils { statusUpdate.text = draft.text; statusUpdate.location = draft.location; statusUpdate.media = draft.media; - if (draft.action_extras instanceof UpdateStatusActionExtra) { - final UpdateStatusActionExtra extra = (UpdateStatusActionExtra) draft.action_extras; + if (draft.action_extras instanceof UpdateStatusActionExtras) { + final UpdateStatusActionExtras extra = (UpdateStatusActionExtras) draft.action_extras; statusUpdate.in_reply_to_status = extra.getInReplyToStatus(); statusUpdate.is_possibly_sensitive = extra.isPossiblySensitive(); statusUpdate.display_coordinates = extra.getDisplayCoordinates(); diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/ContentValuesCreator.java b/twidere/src/main/java/org/mariotaku/twidere/util/ContentValuesCreator.java index b5900bedb..deab0742d 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/ContentValuesCreator.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/ContentValuesCreator.java @@ -42,7 +42,7 @@ import org.mariotaku.twidere.model.ParcelableUser; import org.mariotaku.twidere.model.ParcelableUserMention; import org.mariotaku.twidere.model.ParcelableUserValuesCreator; import org.mariotaku.twidere.model.UserKey; -import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtra; +import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtras; import org.mariotaku.twidere.model.util.ParcelableActivityExtensionsKt; import org.mariotaku.twidere.model.util.ParcelableDirectMessageUtils; import org.mariotaku.twidere.model.util.ParcelableStatusUtils; @@ -121,7 +121,7 @@ public final class ContentValuesCreator implements TwidereConstants { values.put(Drafts.MEDIA, JsonSerializer.serialize(Arrays.asList(mediaArray), ParcelableMediaUpdate.class)); } - final SendDirectMessageActionExtra extra = new SendDirectMessageActionExtra(); + final SendDirectMessageActionExtras extra = new SendDirectMessageActionExtras(); extra.setRecipientId(recipientId); values.put(Drafts.ACTION_EXTRAS, JsonSerializer.serialize(extra)); return values; diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/StatusShortenerInterface.java b/twidere/src/main/java/org/mariotaku/twidere/util/StatusShortenerInterface.java index de0a753cd..dd1b73bdf 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/StatusShortenerInterface.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/StatusShortenerInterface.java @@ -31,7 +31,6 @@ import android.os.RemoteException; import android.util.Log; import org.mariotaku.twidere.BuildConfig; -import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.IStatusShortener; import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.model.ParcelableStatusUpdate; @@ -40,8 +39,10 @@ import org.mariotaku.twidere.model.UserKey; import java.util.List; -public final class StatusShortenerInterface extends AbsServiceInterface - implements Constants { +import static org.mariotaku.twidere.TwidereConstants.LOGTAG; +import static org.mariotaku.twidere.constant.IntentConstants.INTENT_ACTION_EXTENSION_SHORTEN_STATUS; + +public final class StatusShortenerInterface extends AbsServiceInterface { protected StatusShortenerInterface(Context context, String shortenerName, Bundle metaData) { super(context, shortenerName, metaData); diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/io/CountOnlyOutputStream.java b/twidere/src/main/java/org/mariotaku/twidere/util/io/CountOnlyOutputStream.java new file mode 100644 index 000000000..81b492406 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/util/io/CountOnlyOutputStream.java @@ -0,0 +1,21 @@ +package org.mariotaku.twidere.util.io; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Created by mariotaku on 2016/12/8. + */ + +public final class CountOnlyOutputStream extends OutputStream { + private int count; + + @Override + public void write(int i) throws IOException { + count++; + } + + public int getCount() { + return count; + } +} diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt index 77165094c..036402bfc 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt @@ -77,7 +77,7 @@ import org.mariotaku.twidere.constant.KeyboardShortcutConstants import org.mariotaku.twidere.fragment.BaseDialogFragment import org.mariotaku.twidere.fragment.ProgressDialogFragment import org.mariotaku.twidere.model.* -import org.mariotaku.twidere.model.draft.UpdateStatusActionExtra +import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras import org.mariotaku.twidere.model.util.AccountUtils import org.mariotaku.twidere.model.util.ParcelableLocationUtils import org.mariotaku.twidere.preference.ServicePickerPreference @@ -178,8 +178,11 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener override fun onBackPressed() { if (currentTask != null && currentTask!!.status == AsyncTask.Status.RUNNING) return - if (hasComposingStatus()) { - shouldSkipDraft = false + if (!shouldSkipDraft && hasComposingStatus() ) { + saveToDrafts() + Toast.makeText(this, R.string.status_saved_to_draft, Toast.LENGTH_SHORT).show() + shouldSkipDraft = true + finish() } else { shouldSkipDraft = true discardTweet() @@ -429,7 +432,8 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener draft.text = text draft.media = media draft.location = recentLocation - draft.action_extras = UpdateStatusActionExtra().apply { + draft.timestamp = System.currentTimeMillis() + draft.action_extras = UpdateStatusActionExtras().apply { this.inReplyToStatus = this@ComposeActivity.inReplyToStatus this.isPossiblySensitive = this@ComposeActivity.possiblySensitive } @@ -837,8 +841,8 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener addMedia(Arrays.asList(*draft.media)) } recentLocation = draft.location - if (draft.action_extras is UpdateStatusActionExtra) { - val extra = draft.action_extras as UpdateStatusActionExtra? + if (draft.action_extras is UpdateStatusActionExtras) { + val extra = draft.action_extras as UpdateStatusActionExtras? possiblySensitive = extra!!.isPossiblySensitive inReplyToStatus = extra.inReplyToStatus } @@ -871,16 +875,16 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener } when (draft.action_type) { Draft.Action.REPLY -> { - if (draft.action_extras is UpdateStatusActionExtra) { - showReplyLabel((draft.action_extras as UpdateStatusActionExtra).inReplyToStatus) + if (draft.action_extras is UpdateStatusActionExtras) { + showReplyLabel((draft.action_extras as UpdateStatusActionExtras).inReplyToStatus) } else { hideLabel() return false } } Draft.Action.QUOTE -> { - if (draft.action_extras is UpdateStatusActionExtra) { - showQuoteLabel((draft.action_extras as UpdateStatusActionExtra).inReplyToStatus) + if (draft.action_extras is UpdateStatusActionExtras) { + showQuoteLabel((draft.action_extras as UpdateStatusActionExtras).inReplyToStatus) } else { hideLabel() return false @@ -1280,7 +1284,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener update.media = media update.in_reply_to_status = inReplyToStatus update.is_possibly_sensitive = isPossiblySensitive - update.attachment_url = (draft?.action_extras as? UpdateStatusActionExtra)?.attachmentUrl + update.attachment_url = (draft?.action_extras as? UpdateStatusActionExtras)?.attachmentUrl BackgroundOperationService.updateStatusesAsync(this, action, update) if (preferences.getBoolean(KEY_NO_CLOSE_AFTER_TWEET_SENT, false) && inReplyToStatus == null) { possiblySensitive = false diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/DraftsAdapter.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/DraftsAdapter.kt index 3147b263e..5f88ec7ad 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/DraftsAdapter.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/DraftsAdapter.kt @@ -48,7 +48,7 @@ class DraftsAdapter(context: Context) : SimpleCursorAdapter(context, R.layout.li private val mediaPreviewStyle: Int private var mTextSize: Float = 0.toFloat() - private var mIndices: DraftCursorIndices? = null + private var indices: DraftCursorIndices? = null init { GeneralComponentHelper.build(context).inject(this) @@ -58,7 +58,7 @@ class DraftsAdapter(context: Context) : SimpleCursorAdapter(context, R.layout.li override fun bindView(view: View, context: Context, cursor: Cursor) { val holder = view.tag as DraftViewHolder - val indices = mIndices!! + val indices = indices!! val accountKeys = UserKey.arrayOf(cursor.getString(indices.account_keys)) val text = cursor.getString(indices.text) val mediaUpdates = JsonSerializer.parseArray(cursor.getString(indices.media), ParcelableMediaUpdate::class.java) @@ -115,11 +115,16 @@ class DraftsAdapter(context: Context) : SimpleCursorAdapter(context, R.layout.li override fun swapCursor(c: Cursor?): Cursor? { val old = super.swapCursor(c) if (c != null) { - mIndices = DraftCursorIndices(c) + indices = DraftCursorIndices(c) } return old } + fun getDraft(position: Int): Draft { + cursor.moveToPosition(position) + return indices!!.newObject(cursor) + } + private fun getActionName(context: Context, actionType: String): String? { if (TextUtils.isEmpty(actionType)) return context.getString(R.string.update_status) when (actionType) { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/DraftExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/DraftExtensions.kt index fe803430c..3813d0e61 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/DraftExtensions.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/DraftExtensions.kt @@ -2,27 +2,30 @@ package org.mariotaku.twidere.extension import android.content.Context import android.net.Uri +import com.bluelinelabs.logansquare.LoganSquare +import com.nostra13.universalimageloader.utils.IoUtils import org.apache.james.mime4j.dom.Header import org.apache.james.mime4j.dom.MessageServiceFactory import org.apache.james.mime4j.dom.address.Mailbox import org.apache.james.mime4j.dom.field.* -import org.apache.james.mime4j.message.AbstractMessage -import org.apache.james.mime4j.message.BodyPart -import org.apache.james.mime4j.message.MultipartImpl -import org.apache.james.mime4j.message.SimpleContentHandler +import org.apache.james.mime4j.message.* import org.apache.james.mime4j.parser.MimeStreamParser import org.apache.james.mime4j.storage.StorageBodyFactory import org.apache.james.mime4j.stream.BodyDescriptor import org.apache.james.mime4j.stream.MimeConfig +import org.apache.james.mime4j.stream.RawField +import org.apache.james.mime4j.util.MimeUtil import org.mariotaku.ktextension.convert import org.mariotaku.ktextension.toInt import org.mariotaku.ktextension.toString import org.mariotaku.twidere.extension.model.getMimeType -import org.mariotaku.twidere.model.Draft -import org.mariotaku.twidere.model.ParcelableMedia -import org.mariotaku.twidere.model.ParcelableMediaUpdate -import org.mariotaku.twidere.model.UserKey +import org.mariotaku.twidere.model.* +import org.mariotaku.twidere.model.Draft.Action +import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtras +import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras import org.mariotaku.twidere.util.collection.NonEmptyHashMap +import java.io.File +import java.io.FileOutputStream import java.io.InputStream import java.io.OutputStream import java.nio.charset.Charset @@ -45,44 +48,64 @@ fun Draft.writeMimeMessageTo(context: Context, st: OutputStream) { message.date = Date(this.timestamp) message.setFrom(this.account_keys?.map { Mailbox(it.id, it.host) }) + if (message.header == null) { + message.header = HeaderImpl() + } + + this.location?.let { location -> + message.header.addField(RawField("X-GeoLocation", location.toString())) + } + this.action_type?.let { type -> + message.header.addField(RawField("X-Action-Type", type)) + } val multipart = MultipartImpl("mixed") multipart.addBodyPart(BodyPart().apply { - setText(bodyFactory.textBody(this@writeMimeMessageTo.text)) + setText(bodyFactory.textBody(this@writeMimeMessageTo.text, Charsets.UTF_8.name())) }) + + this.action_extras?.let { extras -> + multipart.addBodyPart(BodyPart().apply { + setText(bodyFactory.textBody(LoganSquare.serialize(extras)), "json") + this.filename = "twidere.action.extras.json" + }) + } this.media?.forEach { mediaItem -> multipart.addBodyPart(BodyPart().apply { val uri = Uri.parse(mediaItem.uri) - val storage = storageProvider.store(contentResolver.openInputStream(uri)) val mimeType = mediaItem.getMimeType(contentResolver) ?: "application/octet-stream" val parameters = NonEmptyHashMap() parameters["alt_text"] = mediaItem.alt_text parameters["media_type"] = mediaItem.type.toString() - this.setBody(bodyFactory.binaryBody(storage), mimeType, parameters) + val storage = storageProvider.store(contentResolver.openInputStream(uri)) this.filename = uri.lastPathSegment + this.contentTransferEncoding = MimeUtil.ENC_BASE64 + this.setBody(bodyFactory.binaryBody(storage), mimeType, parameters) }) } message.setMultipart(multipart) writer.writeMessage(message, st) + st.flush() } fun Draft.readMimeMessageFrom(context: Context, st: InputStream) { val config = MimeConfig() val parser = MimeStreamParser(config) - parser.setContentHandler(DraftContentHandler(this)) + parser.isContentDecoding = true + parser.setContentHandler(DraftContentHandler(context, this)) parser.parse(st) } -private class DraftContentHandler(private val draft: Draft) : SimpleContentHandler() { +private class DraftContentHandler(private val context: Context, private val draft: Draft) : SimpleContentHandler() { private val processingStack = Stack() private val mediaList: MutableList = ArrayList() override fun headers(header: Header) { if (processingStack.isEmpty()) { - draft.timestamp = header.getField("Date").convert { + draft.timestamp = header.getField("Date")?.convert { (it as DateTimeField).date.time - } - draft.account_keys = header.getField("From").convert { field -> + } ?: 0 + draft.account_keys = header.getField("From")?.convert { field -> when (field) { is MailboxField -> { return@convert arrayOf(field.mailbox.convert { UserKey(it.localPart, it.domain) }) @@ -95,6 +118,8 @@ private class DraftContentHandler(private val draft: Draft) : SimpleContentHandl } } } + draft.location = header.getField("X-GeoLocation")?.body?.convert(ParcelableLocation::valueOf) + draft.action_type = header.getField("X-Action-Type")?.body } else { processingStack.peek().headers(header) } @@ -108,7 +133,7 @@ private class DraftContentHandler(private val draft: Draft) : SimpleContentHandl } override fun startBodyPart() { - processingStack.push(BodyPartHandler(draft)) + processingStack.push(BodyPartHandler(context, draft)) } override fun body(bd: BodyDescriptor?, `is`: InputStream?) { @@ -131,7 +156,7 @@ private class DraftContentHandler(private val draft: Draft) : SimpleContentHandl } } -private class BodyPartHandler(private val draft: Draft) : SimpleContentHandler() { +private class BodyPartHandler(private val context: Context, private val draft: Draft) : SimpleContentHandler() { internal lateinit var header: Header internal var media: ParcelableMediaUpdate? = null @@ -147,11 +172,32 @@ private class BodyPartHandler(private val draft: Draft) : SimpleContentHandler() val contentDisposition = header.getField("Content-Disposition") as? ContentDispositionField if (contentDisposition != null && contentDisposition.isAttachment) { when (contentDisposition.filename) { + "twidere.action.extras.json" -> { + draft.action_extras = when (draft.action_type) { + "0", "1", Action.UPDATE_STATUS, Action.REPLY, Action.QUOTE -> { + LoganSquare.parse(st, UpdateStatusActionExtras::class.java) + } + "2", Action.SEND_DIRECT_MESSAGE -> { + LoganSquare.parse(st, SendDirectMessageActionExtras::class.java) + } + else -> { + null + } + } + } else -> { val contentType = header.getField("Content-Type") as? ContentTypeField + val filename = contentDisposition.filename ?: return + val mediaFile = File(context.filesDir, filename) media = ParcelableMediaUpdate().apply { + bd.transferEncoding this.type = contentType?.getParameter("media_type").toInt(ParcelableMedia.Type.UNKNOWN) this.alt_text = contentType?.getParameter("alt_text") + FileOutputStream(mediaFile).use { + IoUtils.copyStream(st, it, null) + it.flush() + } + this.uri = Uri.fromFile(mediaFile).toString() } } } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/DraftsFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/DraftsFragment.kt index 4c2bdc28f..c7560e250 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/DraftsFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/DraftsFragment.kt @@ -27,9 +27,11 @@ import android.content.DialogInterface import android.content.DialogInterface.OnClickListener import android.content.Intent import android.database.Cursor +import android.media.MediaScannerConnection import android.net.Uri import android.os.AsyncTask import android.os.Bundle +import android.os.Environment import android.os.Handler import android.support.v4.app.DialogFragment import android.support.v4.app.FragmentActivity @@ -43,7 +45,10 @@ import android.widget.AbsListView.MultiChoiceModeListener import android.widget.AdapterView import android.widget.AdapterView.OnItemClickListener import android.widget.ListView +import android.widget.Toast import kotlinx.android.synthetic.main.fragment_drafts.* +import nl.komponents.kovenant.task +import nl.komponents.kovenant.ui.successUi import org.mariotaku.sqliteqb.library.Columns.Column import org.mariotaku.sqliteqb.library.Expression import org.mariotaku.sqliteqb.library.RawItemArray @@ -52,10 +57,10 @@ import org.mariotaku.twidere.TwidereConstants.* import org.mariotaku.twidere.activity.iface.IExtendedActivity import org.mariotaku.twidere.adapter.DraftsAdapter import org.mariotaku.twidere.constant.IntentConstants +import org.mariotaku.twidere.extension.writeMimeMessageTo import org.mariotaku.twidere.model.Draft -import org.mariotaku.twidere.model.DraftCursorIndices import org.mariotaku.twidere.model.ParcelableMediaUpdate -import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtra +import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtras import org.mariotaku.twidere.model.util.ParcelableStatusUpdateUtils import org.mariotaku.twidere.provider.TwidereDataStore.Drafts import org.mariotaku.twidere.service.BackgroundOperationService @@ -63,6 +68,7 @@ import org.mariotaku.twidere.util.AsyncTaskUtils import org.mariotaku.twidere.util.JsonSerializer import org.mariotaku.twidere.util.Utils.getDefaultTextSize import java.io.File +import java.io.FileOutputStream import java.util.* class DraftsFragment : BaseSupportFragment(), LoaderCallbacks, OnItemClickListener, MultiChoiceModeListener { @@ -85,27 +91,52 @@ class DraftsFragment : BaseSupportFragment(), LoaderCallbacks, OnItemCl R.id.delete -> { val f = DeleteDraftsConfirmDialogFragment() val args = Bundle() - args.putLongArray(IntentConstants.EXTRA_IDS, listView!!.checkedItemIds) + args.putLongArray(IntentConstants.EXTRA_IDS, listView.checkedItemIds) f.arguments = args f.show(childFragmentManager, "delete_drafts_confirm") } R.id.send -> { - val c = adapter!!.cursor - if (c == null || c.isClosed) return false - val checked = listView!!.checkedItemPositions + val checked = listView.checkedItemPositions val list = ArrayList() - val indices = DraftCursorIndices(c) for (i in 0 until checked.size()) { - if (checked.valueAt(i) && c.moveToPosition(checked.keyAt(i))) { - list.add(indices.newObject(c)) + val position = checked.keyAt(i) + if (checked.valueAt(i)) { + list.add(adapter!!.getDraft(position)) } } if (sendDrafts(list)) { val where = Expression.`in`(Column(Drafts._ID), - RawItemArray(listView!!.checkedItemIds)) + RawItemArray(listView.checkedItemIds)) contentResolver.delete(Drafts.CONTENT_URI, where.sql, null) } } + R.id.save -> { + val checked = listView.checkedItemPositions + val drafts = ArrayList() + for (i in 0 until checked.size()) { + val position = checked.keyAt(i) + if (checked.valueAt(i)) { + drafts.add(adapter!!.getDraft(position)) + } + } + task { + val pubDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + val paths = drafts.map { draft -> + val name = "${draft.timestamp}.eml" + val destFile = File(pubDir, name) + FileOutputStream(destFile).use { + draft.writeMimeMessageTo(context, it) + it.flush() + } + return@map destFile.absolutePath + } + MediaScannerConnection.scanFile(context, paths.toTypedArray(), null, null) + }.successUi { + Toast.makeText(context, R.string.draft_saved, Toast.LENGTH_SHORT).show() + }.fail { ex -> + Log.w(LOGTAG, ex) + } + } else -> { return false } @@ -140,9 +171,7 @@ class DraftsFragment : BaseSupportFragment(), LoaderCallbacks, OnItemCl } override fun onItemClick(view: AdapterView<*>, child: View, position: Int, id: Long) { - val c = adapter!!.cursor - if (c == null || c.isClosed || !c.moveToPosition(position)) return - val item = DraftCursorIndices.fromCursor(c) + val item = adapter!!.getDraft(position) if (TextUtils.isEmpty(item.action_type)) { editDraft(item) return @@ -162,13 +191,13 @@ class DraftsFragment : BaseSupportFragment(), LoaderCallbacks, OnItemCl super.onActivityCreated(savedInstanceState) adapter = DraftsAdapter(activity) adapter!!.setTextSize(preferences.getInt(KEY_TEXT_SIZE, getDefaultTextSize(activity)).toFloat()) - listView!!.adapter = adapter - listView!!.emptyView = emptyView - listView!!.onItemClickListener = this - listView!!.choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL - listView!!.setMultiChoiceModeListener(this) - emptyIcon!!.setImageResource(R.drawable.ic_info_draft) - emptyText!!.setText(R.string.drafts_hint_messages) + listView.adapter = adapter + listView.emptyView = emptyView + listView.onItemClickListener = this + listView.choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL + listView.setMultiChoiceModeListener(this) + emptyIcon.setImageResource(R.drawable.ic_info_draft) + emptyText.setText(R.string.drafts_hint_messages) loaderManager.initLoader(0, null, this) setListShown(false) } @@ -207,8 +236,8 @@ class DraftsFragment : BaseSupportFragment(), LoaderCallbacks, OnItemCl } Draft.Action.SEND_DIRECT_MESSAGE_COMPAT, Draft.Action.SEND_DIRECT_MESSAGE -> { var recipientId: String? = null - if (item.action_extras is SendDirectMessageActionExtra) { - recipientId = (item.action_extras as SendDirectMessageActionExtra).recipientId + if (item.action_extras is SendDirectMessageActionExtras) { + recipientId = (item.action_extras as SendDirectMessageActionExtras).recipientId } if (item.account_keys?.isEmpty() ?: true || recipientId == null) { continue@loop @@ -224,7 +253,7 @@ class DraftsFragment : BaseSupportFragment(), LoaderCallbacks, OnItemCl private fun updateTitle(mode: ActionMode?) { if (listView == null || mode == null) return - val count = listView!!.checkedItemCount + val count = listView.checkedItemCount mode.title = resources.getQuantityString(R.plurals.Nitems_selected, count, count) } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/model/util/ParcelableStatusUtils.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/model/util/ParcelableStatusUtils.kt index 73c4f5a70..e3d901e79 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/model/util/ParcelableStatusUtils.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/model/util/ParcelableStatusUtils.kt @@ -199,7 +199,7 @@ object ParcelableStatusUtils { private fun getPlaceFullName(status: Status): String? { val place = status.place if (place != null) return place.fullName - val location = status.location + val location = status.location ?: return null if (ParcelableLocation.valueOf(location) == null) { return location } @@ -211,7 +211,7 @@ object ParcelableStatusUtils { if (geoLocation != null) { return ParcelableLocationUtils.fromGeoLocation(geoLocation) } - val locationString = status.location + val locationString = status.location ?: return null val location = ParcelableLocation.valueOf(locationString) if (location != null) { return location diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/service/AccountAuthenticatorService.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/service/AccountAuthenticatorService.kt index 3c1b25e31..f0e47b000 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/service/AccountAuthenticatorService.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/service/AccountAuthenticatorService.kt @@ -31,7 +31,6 @@ class AccountAuthenticatorService : Service() { internal class TwidereAccountAuthenticator(val context: Context) : AbstractAccountAuthenticator(context) { - // TODO: Make SignInActivity comply with AccountAuthenticatorActivity override fun addAccount(response: AccountAuthenticatorResponse, accountType: String, authTokenType: String?, requiredFeatures: Array?, options: Bundle?): Bundle { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/service/BackgroundOperationService.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/service/BackgroundOperationService.kt index f98a1ca6e..416358e04 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/service/BackgroundOperationService.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/service/BackgroundOperationService.kt @@ -62,7 +62,7 @@ import org.mariotaku.twidere.TwidereConstants.* import org.mariotaku.twidere.annotation.AccountType import org.mariotaku.twidere.extension.newMicroBlogInstance import org.mariotaku.twidere.model.* -import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtra +import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtras import org.mariotaku.twidere.model.util.AccountUtils import org.mariotaku.twidere.model.util.ParcelableDirectMessageUtils import org.mariotaku.twidere.model.util.ParcelableStatusUpdateUtils @@ -173,7 +173,7 @@ class BackgroundOperationService : IntentService("background_operation"), Consta updateStatuses(item.action_type, ParcelableStatusUpdateUtils.fromDraftItem(this, item)) } Draft.Action.SEND_DIRECT_MESSAGE_COMPAT, Draft.Action.SEND_DIRECT_MESSAGE -> { - val recipientId = (item.action_extras as? SendDirectMessageActionExtra)?.recipientId ?: return + val recipientId = (item.action_extras as? SendDirectMessageActionExtras)?.recipientId ?: return if (item.account_keys?.isEmpty() ?: true) { return } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/UpdateStatusTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/UpdateStatusTask.kt index db8ff1433..f34438a77 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/UpdateStatusTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/UpdateStatusTask.kt @@ -32,7 +32,7 @@ import org.mariotaku.twidere.TwidereConstants.* import org.mariotaku.twidere.annotation.AccountType import org.mariotaku.twidere.app.TwidereApplication import org.mariotaku.twidere.model.* -import org.mariotaku.twidere.model.draft.UpdateStatusActionExtra +import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras import org.mariotaku.twidere.model.util.ParcelableLocationUtils import org.mariotaku.twidere.model.util.ParcelableStatusUtils import org.mariotaku.twidere.preference.ServicePickerPreference @@ -519,7 +519,8 @@ class UpdateStatusTask( draft.text = statusUpdate.text draft.location = statusUpdate.location draft.media = statusUpdate.media - draft.action_extras = UpdateStatusActionExtra().apply { + draft.timestamp = System.currentTimeMillis() + draft.action_extras = UpdateStatusActionExtras().apply { inReplyToStatus = statusUpdate.in_reply_to_status isPossiblySensitive = statusUpdate.is_possibly_sensitive isRepostStatusId = statusUpdate.repost_status_id diff --git a/twidere/src/main/res/menu/action_multi_select_drafts.xml b/twidere/src/main/res/menu/action_multi_select_drafts.xml index c56bdfbd8..aac4ea382 100644 --- a/twidere/src/main/res/menu/action_multi_select_drafts.xml +++ b/twidere/src/main/res/menu/action_multi_select_drafts.xml @@ -5,12 +5,17 @@ + android:title="@string/delete" + app:showAsAction="ifRoom|withText"/> - + android:title="@string/send" + app:showAsAction="ifRoom|withText"/> + \ No newline at end of file