diff options
Diffstat (limited to 'src')
67 files changed, 2646 insertions, 1894 deletions
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 325459850..f1f665d74 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -1,8 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest - package="eu.siacs.conversations" - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="eu.siacs.conversations" > <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> @@ -29,10 +28,15 @@ <receiver android:name=".services.EventReceiver"> <intent-filter> - <action android:name="android.intent.action.BOOT_COMPLETED"/> - <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/> - <action android:name="android.intent.action.ACTION_SHUTDOWN"/> - <action android:name="android.media.RINGER_MODE_CHANGED"/> + <action android:name="android.intent.action.BOOT_COMPLETED" /> + <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> + <action android:name="android.intent.action.ACTION_SHUTDOWN" /> + <action android:name="android.intent.action.MY_PACKAGE_REPLACED" /> + <action android:name="android.media.RINGER_MODE_CHANGED" /> + </intent-filter> + <intent-filter> + <action android:name="android.intent.action.PACKAGE_REPLACED" /> + <data android:scheme="package" android:path="eu.siacs.conversations" /> </intent-filter> </receiver> @@ -52,7 +56,7 @@ android:name=".ui.StartConversationActivity" android:configChanges="orientation|screenSize" android:label="@string/title_activity_start_conversation" - android:launchMode="singleTask"> + android:launchMode="singleTask" > <intent-filter> <action android:name="android.intent.action.SENDTO"/> @@ -145,11 +149,11 @@ <activity android:name=".ui.TrustKeysActivity" android:label="@string/trust_omemo_fingerprints" - android:windowSoftInputMode="stateAlwaysHidden"/> + android:windowSoftInputMode="stateAlwaysHidden" /> <activity android:name="de.duenndns.ssl.MemorizingActivity" android:theme="@style/ConversationsTheme" - tools:replace="android:theme"/> + tools:replace="android:theme" /> <activity android:name=".ui.AboutActivity" android:label="@string/title_activity_about" @@ -158,8 +162,15 @@ android:name="android.support.PARENT_ACTIVITY" android:value="eu.siacs.conversations.ui.SettingsActivity"/> </activity> - - <service android:name=".services.ExportLogsService"/> + <activity + android:name=".ui.UpdaterActivity" + android:screenOrientation="portrait" + android:theme="@style/ConversationsTheme" + android:label="@string/title_activity_updater" > + </activity> + <service android:name=".services.UpdaterWebService" /> + <service android:name=".services.CheckAppVersionService" /> + <service android:name=".services.ExportLogsService" /> </application> </manifest> diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index 14605cf09..9361fb0d6 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -9,11 +9,12 @@ public final class Config { public static final String LOGTAG = "conversations"; - public static final String DOMAIN_LOCK = null; //only allow account creation for this domain + public static final String DOMAIN_LOCK = "pix-art.de"; //only allow account creation for this domain public static final boolean DISALLOW_REGISTRATION_IN_UI = false; //hide the register checkbox - public static final boolean HIDE_PGP_IN_UI = false; //some more consumer focused clients might want to disable OpenPGP + public static final boolean HIDE_PGP_IN_UI = true; //some more consumer focused clients might want to disable OpenPGP + public static final boolean HIDE_ENCRYPTION_IN_UI = false; //completely hide encryption options in menu bar - public static final boolean LEGACY_NAMESPACE_HTTP_UPLOAD = false; + public static final boolean LEGACY_NAMESPACE_HTTP_UPLOAD = false; // true = 'eu:siacs:conversations:http:upload' false = 'urn:xmpp:http:upload' public static final int PING_MAX_INTERVAL = 300; public static final int PING_MIN_INTERVAL = 30; @@ -23,7 +24,7 @@ public final class Config { public static final int CARBON_GRACE_PERIOD = 90; public static final int MINI_GRACE_PERIOD = 750; - public static final int AVATAR_SIZE = 192; + public static final int AVATAR_SIZE = 640; public static final Bitmap.CompressFormat AVATAR_FORMAT = Bitmap.CompressFormat.WEBP; public static final int IMAGE_SIZE = 1920; @@ -57,7 +58,10 @@ public final class Config { public static final int MAM_MAX_MESSAGES = 500; public static final ChatState DEFAULT_CHATSTATE = ChatState.ACTIVE; - public static final int TYPING_TIMEOUT = 8; + public static final int TYPING_TIMEOUT = 5; + + public static final String UPDATE_URL = "http://xmpp.pix-art.de/Conversations/update/"; + public static final long UPDATE_CHECK_TIMER = 24 * 60 * 60; // in seconds public static final String ENABLED_CIPHERS[] = { "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java index f924c05a1..f5fa88d7a 100644 --- a/src/main/java/eu/siacs/conversations/entities/Contact.java +++ b/src/main/java/eu/siacs/conversations/entities/Contact.java @@ -149,6 +149,9 @@ public class Contact implements ListItem, Blockable { if (isBlocked()) { tags.add(new Tag("blocked", 0xff2e2f3b)); } + if (!getMostAvailableResource().equals("")){ + tags.add(new Tag(getMostAvailableResource(), 0xff37b8a9)); + } return tags; } @@ -239,6 +242,10 @@ public class Contact implements ListItem, Blockable { return this.presences.getMostAvailableStatus(); } + public String getMostAvailableResource() { + return this.presences.getMostAvailableResource(); + } + public boolean setPhotoUri(String uri) { if (uri != null && !uri.equals(this.photoUri)) { this.photoUri = uri; diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index 473ef0fe0..ee1eb2110 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -20,6 +20,7 @@ import java.util.Iterator; import java.util.List; import eu.siacs.conversations.Config; +import eu.siacs.conversations.R; import eu.siacs.conversations.xmpp.chatstate.ChatState; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; @@ -367,6 +368,19 @@ public class Conversation extends AbstractEntity implements Blockable { return this.getContact().getDisplayName(); } } + + public String getParticipants() { + if (getMode() == MODE_MULTI) { + String generatedName = getMucOptions().createNameFromParticipants(); + if (generatedName != null) { + return generatedName; + } else { + return null; + } + } else { + return null; + } + } public String getAccountUuid() { return this.accountUuid; diff --git a/src/main/java/eu/siacs/conversations/entities/Presences.java b/src/main/java/eu/siacs/conversations/entities/Presences.java index 4729a11b9..a1e90d170 100644 --- a/src/main/java/eu/siacs/conversations/entities/Presences.java +++ b/src/main/java/eu/siacs/conversations/entities/Presences.java @@ -52,6 +52,21 @@ public class Presences { return status; } + public String getMostAvailableResource() { + int status = OFFLINE; + String resource = ""; + synchronized (this.presences) { + Iterator<Entry<String, Integer>> it = presences.entrySet().iterator(); + while (it.hasNext()) { + Entry<String, Integer> entry = it.next(); + if (entry.getValue() < status) + status = entry.getValue(); + resource = entry.getKey(); + } + } + return resource; + } + public static int parseShow(Element show) { if ((show == null) || (show.getContent() == null)) { return Presences.ONLINE; diff --git a/src/main/java/eu/siacs/conversations/entities/Transferable.java b/src/main/java/eu/siacs/conversations/entities/Transferable.java index 016c81bdf..859cda658 100644 --- a/src/main/java/eu/siacs/conversations/entities/Transferable.java +++ b/src/main/java/eu/siacs/conversations/entities/Transferable.java @@ -2,9 +2,58 @@ package eu.siacs.conversations.entities; public interface Transferable { - String[] VALID_IMAGE_EXTENSIONS = {"webp", "jpeg", "jpg", "png", "jpe"}; - String[] VALID_CRYPTO_EXTENSIONS = {"pgp", "gpg", "otr"}; - String[] WELL_KNOWN_EXTENSIONS = {"pdf","m4a","mp4"}; + String[] VALID_IMAGE_EXTENSIONS = { + "webp", + "jpeg", + "jpg", + "png", + "jpe", + "gif", + "tif" + }; + String[] VALID_CRYPTO_EXTENSIONS = { + "pgp", + "gpg", + "otr" + }; + String[] WELL_KNOWN_EXTENSIONS = { + //documents + "pdf", + "doc", + "docx", + "txt", + //audio + "m4a", + "m4b", + "mp3", + "mp2", + "wav", + "aac", + "aif", + "aiff", + "aifc", + "mid", + "midi", + "3gpp", + //video + "avi", + "mp4", + "mpeg", + "mpg", + "mpe", + "mov", + "3gp", + //applications + "apk", + //contact + "vcf", + //calendar + "ics", + //compressed + "zip", + "rar", + + }; int STATUS_UNKNOWN = 0x200; int STATUS_CHECKING = 0x201; diff --git a/src/main/java/eu/siacs/conversations/services/AvatarService.java b/src/main/java/eu/siacs/conversations/services/AvatarService.java index 3cd32d795..00897c9ae 100644 --- a/src/main/java/eu/siacs/conversations/services/AvatarService.java +++ b/src/main/java/eu/siacs/conversations/services/AvatarService.java @@ -45,12 +45,12 @@ public class AvatarService { if (avatar != null || cachedOnly) { return avatar; } - if (contact.getProfilePhoto() != null) { - avatar = mXmppConnectionService.getFileBackend().cropCenterSquare(Uri.parse(contact.getProfilePhoto()), size); - } if (avatar == null && contact.getAvatar() != null) { avatar = mXmppConnectionService.getFileBackend().getAvatar(contact.getAvatar(), size); } + if (avatar == null && contact.getProfilePhoto() != null) { + avatar = mXmppConnectionService.getFileBackend().cropCenterSquare(Uri.parse(contact.getProfilePhoto()), size); + } if (avatar == null) { avatar = get(contact.getDisplayName(), size, cachedOnly); } @@ -283,11 +283,11 @@ public class AvatarService { Contact contact = user.getContact(); if (contact != null) { Uri uri = null; - if (contact.getProfilePhoto() != null) { - uri = Uri.parse(contact.getProfilePhoto()); - } else if (contact.getAvatar() != null) { + if (contact.getAvatar() != null) { uri = mXmppConnectionService.getFileBackend().getAvatarUri( contact.getAvatar()); + } else if (contact.getProfilePhoto() != null) { + uri = Uri.parse(contact.getProfilePhoto()); } if (uri != null) { Bitmap bitmap = mXmppConnectionService.getFileBackend() diff --git a/src/main/java/eu/siacs/conversations/services/CheckAppVersionService.java b/src/main/java/eu/siacs/conversations/services/CheckAppVersionService.java new file mode 100644 index 000000000..33faeaeca --- /dev/null +++ b/src/main/java/eu/siacs/conversations/services/CheckAppVersionService.java @@ -0,0 +1,43 @@ +package eu.siacs.conversations.services; + +import com.google.gson.JsonObject; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class CheckAppVersionService extends HttpServlet { + private static final long serialVersionUID = 1L; + + public CheckAppVersionService() { + super(); + } + + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + doPost(request,response); + } + + protected void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + PrintWriter out = response.getWriter(); + response.setContentType("text/html"); + + //send a JSON response with the app Version and file URI + JsonObject myObj = new JsonObject(); + myObj.addProperty("success", false); + myObj.addProperty("latestVersionCode", 2); + myObj.addProperty("latestVersion", "1.0.0"); + myObj.addProperty("changelog", ""); + myObj.addProperty("appURI", ""); + out.println(myObj.toString()); + out.close(); + + } + +} diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java index 125b1c268..3cb962ac6 100644 --- a/src/main/java/eu/siacs/conversations/services/NotificationService.java +++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java @@ -60,12 +60,12 @@ public class NotificationService { public boolean notify(final Message message) { return (message.getStatus() == Message.STATUS_RECEIVED) - && notificationsEnabled() - && !message.getConversation().isMuted() - && (message.getConversation().isPnNA() - || conferenceNotificationsEnabled() - || wasHighlightedOrPrivate(message) - ); + && notificationsEnabled() + && !message.getConversation().isMuted() + && (message.getConversation().isPnNA() + || conferenceNotificationsEnabled() + || wasHighlightedOrPrivate(message) + ); } public void notifyPebble(final Message message) { @@ -179,7 +179,7 @@ public class NotificationService { public void updateNotification(final boolean notify) { final NotificationManager notificationManager = (NotificationManager) mXmppConnectionService - .getSystemService(Context.NOTIFICATION_SERVICE); + .getSystemService(Context.NOTIFICATION_SERVICE); final SharedPreferences preferences = mXmppConnectionService.getPreferences(); final String ringtone = preferences.getString("notification_ringtone", null); @@ -235,7 +235,7 @@ public class NotificationService { conversation = messages.get(0).getConversation(); final String name = conversation.getName(); style.addLine(Html.fromHtml("<b>" + name + "</b> " - + UIHelper.getMessagePreview(mXmppConnectionService,messages.get(0)).first)); + + UIHelper.getMessagePreview(mXmppConnectionService,messages.get(0)).first)); names.append(name); names.append(", "); } @@ -275,9 +275,9 @@ public class NotificationService { Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? R.drawable.ic_file_download_white_24dp : R.drawable.ic_action_download, mXmppConnectionService.getResources().getString(R.string.download_x_file, - UIHelper.getFileDescriptionString(mXmppConnectionService, message)), + UIHelper.getFileDescriptionString(mXmppConnectionService, message)), createDownloadIntent(message) - ); + ); } if ((message = getFirstLocationMessage(messages)) != null) { mBuilder.addAction(R.drawable.ic_room_white_24dp, @@ -290,16 +290,16 @@ public class NotificationService { } private void modifyForImage(final Builder builder, final Message message, - final ArrayList<Message> messages, final boolean notify) { + final ArrayList<Message> messages, final boolean notify) { try { final Bitmap bitmap = mXmppConnectionService.getFileBackend() - .getThumbnail(message, getPixel(288), false); + .getThumbnail(message, getPixel(200), false); final ArrayList<Message> tmp = new ArrayList<>(); for (final Message msg : messages) { if (msg.getType() == Message.TYPE_TEXT && msg.getTransferable() == null) { tmp.add(msg); - } + } } final BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle(); bigPictureStyle.bigPicture(bitmap); @@ -318,7 +318,7 @@ public class NotificationService { } private void modifyForTextOnly(final Builder builder, - final ArrayList<Message> messages, final boolean notify) { + final ArrayList<Message> messages, final boolean notify) { builder.setStyle(new NotificationCompat.BigTextStyle().bigText(getMergedBodies(messages))); builder.setContentText(UIHelper.getMessagePreview(mXmppConnectionService,messages.get(0)).first); if (notify) { @@ -333,7 +333,7 @@ public class NotificationService { && message.getEncryption() != Message.ENCRYPTION_PGP && message.getFileParams().height > 0) { return message; - } + } } return null; } @@ -380,7 +380,7 @@ public class NotificationService { private PendingIntent createContentIntent(final String conversationUuid, final String downloadMessageUuid) { final TaskStackBuilder stackBuilder = TaskStackBuilder - .create(mXmppConnectionService); + .create(mXmppConnectionService); stackBuilder.addParentStack(ConversationActivity.class); final Intent viewConversationIntent = new Intent(mXmppConnectionService, @@ -468,7 +468,7 @@ public class NotificationService { private int getPixel(final int dp) { final DisplayMetrics metrics = mXmppConnectionService.getResources() - .getDisplayMetrics(); + .getDisplayMetrics(); return ((int) (dp * metrics.density)); } @@ -478,7 +478,7 @@ public class NotificationService { private boolean inMiniGracePeriod(final Account account) { final int miniGrace = account.getStatus() == Account.State.ONLINE ? Config.MINI_GRACE_PERIOD - : Config.MINI_GRACE_PERIOD * 2; + : Config.MINI_GRACE_PERIOD * 2; return SystemClock.elapsedRealtime() < (this.mLastNotification + miniGrace); } diff --git a/src/main/java/eu/siacs/conversations/services/UpdaterWebService.java b/src/main/java/eu/siacs/conversations/services/UpdaterWebService.java new file mode 100644 index 000000000..3ff6641aa --- /dev/null +++ b/src/main/java/eu/siacs/conversations/services/UpdaterWebService.java @@ -0,0 +1,102 @@ +package eu.siacs.conversations.services; + +import android.app.IntentService; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.util.Log; + +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.StatusLine; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.conn.params.ConnManagerParams; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.params.HttpConnectionParams; +import org.apache.http.params.HttpParams; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import eu.siacs.conversations.Config; +import eu.siacs.conversations.R; +import eu.siacs.conversations.ui.UpdaterActivity.UpdateReceiver; + +public class UpdaterWebService extends IntentService{ + public static final String REQUEST_STRING = ""; + public static final String RESPONSE_MESSAGE = ""; + + private String URL = null; + public static final int REGISTRATION_TIMEOUT = 3 * 1000; + public static final int WAIT_TIMEOUT = 30 * 1000; + + public UpdaterWebService() { + super("UpdaterWebService"); + } + + @Override + protected void onHandleIntent(Intent intent) { + + String requestString = intent.getStringExtra(REQUEST_STRING); + Log.d(Config.LOGTAG, "AppUpdater: " + requestString); + String responseMessage; + PackageInfo pInfo = null; + try { + pInfo = getPackageManager().getPackageInfo(getPackageName(), 0); + } + catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + //get the app version Name for display + final String versionName = pInfo.versionName; + + try { + + URL = requestString; + HttpClient httpclient = new DefaultHttpClient(); + HttpParams params = httpclient.getParams(); + + HttpConnectionParams.setConnectionTimeout(params, REGISTRATION_TIMEOUT); + HttpConnectionParams.setSoTimeout(params, WAIT_TIMEOUT); + ConnManagerParams.setTimeout(params, WAIT_TIMEOUT); + + HttpGet httpGet = new HttpGet(URL); + httpGet.setHeader("User-Agent", getString(R.string.app_name) + " " + versionName); + HttpResponse response = httpclient.execute(httpGet); + + StatusLine statusLine = response.getStatusLine(); + Log.d(Config.LOGTAG, "AppUpdater: HTTP Status Code: " + statusLine.getStatusCode()); + if(statusLine.getStatusCode() == HttpStatus.SC_OK){ + ByteArrayOutputStream out = new ByteArrayOutputStream(); + response.getEntity().writeTo(out); + out.close(); + responseMessage = out.toString(); + } else { + Log.e(Config.LOGTAG, "AppUpdater: HTTP1:" + statusLine.getReasonPhrase()); + response.getEntity().getContent().close(); + throw new IOException(statusLine.getReasonPhrase()); + } + + } catch (ClientProtocolException e) { + Log.e(Config.LOGTAG, "AppUpdater: HTTP2:" + e); + responseMessage = ""; + } catch (IOException e) { + Log.e(Config.LOGTAG, "AppUpdater: HTTP3:" + e); + responseMessage = ""; + }catch (Exception e) { + Log.e(Config.LOGTAG, "AppUpdater: HTTP4:" + e); + responseMessage = ""; + } + + + Intent broadcastIntent = new Intent(); + broadcastIntent.setAction(UpdateReceiver.PROCESS_RESPONSE); + broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT); + broadcastIntent.putExtra(RESPONSE_MESSAGE, responseMessage); + sendBroadcast(broadcastIntent); + + } + +}
\ No newline at end of file diff --git a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java index 20195256f..82931b4ab 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java @@ -538,11 +538,11 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers Bitmap bm; Contact contact = user.getContact(); if (contact != null) { - bm = avatarService().get(contact, getPixel(48)); + bm = avatarService().get(contact, getPixel(56)); tvDisplayName.setText(contact.getDisplayName()); tvStatus.setText(user.getName() + " \u2022 " + getStatus(user)); } else { - bm = avatarService().get(user.getName(), getPixel(48)); + bm = avatarService().get(user.getName(), getPixel(56)); tvDisplayName.setText(user.getName()); tvStatus.setText(getStatus(user)); diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java index ebac6feba..730b63056 100644 --- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java @@ -376,7 +376,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd account = contact.getAccount().getJid().toBareJid().toString(); } accountJidTv.setText(getString(R.string.using_account, account)); - badge.setImageBitmap(avatarService().get(contact, getPixel(72))); + badge.setImageBitmap(avatarService().get(contact, getPixel(Config.AVATAR_SIZE))); badge.setOnClickListener(this.onBadgeClick); keys.removeAllViews(); diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java index 1345ada54..b5f8480f3 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java @@ -6,10 +6,12 @@ import android.app.AlertDialog; import android.app.FragmentTransaction; import android.app.PendingIntent; import android.content.ClipData; +import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.IntentSender.SendIntentException; +import android.content.SharedPreferences; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -47,1422 +49,1475 @@ import eu.siacs.conversations.entities.Blockable; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.entities.Presences; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate; import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate; import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate; import eu.siacs.conversations.ui.adapter.ConversationAdapter; import eu.siacs.conversations.utils.ExceptionHelper; +import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xmpp.OnUpdateBlocklist; +import eu.siacs.conversations.xmpp.chatstate.ChatState; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; public class ConversationActivity extends XmppActivity - implements OnAccountUpdate, OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist, XmppConnectionService.OnShowErrorToast { - - public static final String ACTION_DOWNLOAD = "eu.siacs.conversations.action.DOWNLOAD"; - - public static final String VIEW_CONVERSATION = "viewConversation"; - public static final String CONVERSATION = "conversationUuid"; - public static final String MESSAGE = "messageUuid"; - public static final String TEXT = "text"; - public static final String NICK = "nick"; - public static final String PRIVATE_MESSAGE = "pm"; - - public static final int REQUEST_SEND_MESSAGE = 0x0201; - public static final int REQUEST_DECRYPT_PGP = 0x0202; - public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207; - public static final int REQUEST_TRUST_KEYS_TEXT = 0x0208; - public static final int REQUEST_TRUST_KEYS_MENU = 0x0209; - public static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301; - public static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302; - public static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303; - public static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0304; - public static final int ATTACHMENT_CHOICE_LOCATION = 0x0305; - public static final int ATTACHMENT_CHOICE_INVALID = 0x0306; - private static final String STATE_OPEN_CONVERSATION = "state_open_conversation"; - private static final String STATE_PANEL_OPEN = "state_panel_open"; - private static final String STATE_PENDING_URI = "state_pending_uri"; - - private String mOpenConverstaion = null; - private boolean mPanelOpen = true; - final private List<Uri> mPendingImageUris = new ArrayList<>(); - final private List<Uri> mPendingFileUris = new ArrayList<>(); - private Uri mPendingGeoUri = null; - private boolean forbidProcessingPendings = false; - - private boolean conversationWasSelectedByKeyboard = false; - - private View mContentView; - - private List<Conversation> conversationList = new ArrayList<>(); - private Conversation swipedConversation = null; - private Conversation mSelectedConversation = null; - private EnhancedListView listView; - private ConversationFragment mConversationFragment; - - private ArrayAdapter<Conversation> listAdapter; - - private Toast prepareFileToast; - - private boolean mActivityPaused = false; - private AtomicBoolean mRedirected = new AtomicBoolean(false); - - public Conversation getSelectedConversation() { - return this.mSelectedConversation; - } - - public void setSelectedConversation(Conversation conversation) { - this.mSelectedConversation = conversation; - } - - public void showConversationsOverview() { - if (mContentView instanceof SlidingPaneLayout) { - SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView; - mSlidingPaneLayout.openPane(); - } - } - - @Override - protected String getShareableUri() { - Conversation conversation = getSelectedConversation(); - if (conversation != null) { - return conversation.getAccount().getShareableUri(); - } else { - return ""; - } - } - - public void hideConversationsOverview() { - if (mContentView instanceof SlidingPaneLayout) { - SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView; - mSlidingPaneLayout.closePane(); - } - } - - public boolean isConversationsOverviewHideable() { - if (mContentView instanceof SlidingPaneLayout) { - SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView; - return mSlidingPaneLayout.isSlideable(); - } else { - return false; - } - } - - public boolean isConversationsOverviewVisable() { - if (mContentView instanceof SlidingPaneLayout) { - SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView; - return mSlidingPaneLayout.isOpen(); - } else { - return true; - } - } - - @Override - protected void onCreate(final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (savedInstanceState != null) { - mOpenConverstaion = savedInstanceState.getString(STATE_OPEN_CONVERSATION, null); - mPanelOpen = savedInstanceState.getBoolean(STATE_PANEL_OPEN, true); - String pending = savedInstanceState.getString(STATE_PENDING_URI, null); - if (pending != null) { - mPendingImageUris.clear(); - mPendingImageUris.add(Uri.parse(pending)); - } - } - - setContentView(R.layout.fragment_conversations_overview); - - this.mConversationFragment = new ConversationFragment(); - FragmentTransaction transaction = getFragmentManager().beginTransaction(); - transaction.replace(R.id.selected_conversation, this.mConversationFragment, "conversation"); - transaction.commit(); - - listView = (EnhancedListView) findViewById(R.id.list); - this.listAdapter = new ConversationAdapter(this, conversationList); - listView.setAdapter(this.listAdapter); - - if (getActionBar() != null) { - getActionBar().setDisplayHomeAsUpEnabled(false); - getActionBar().setHomeButtonEnabled(false); - } - - listView.setOnItemClickListener(new OnItemClickListener() { - - @Override - public void onItemClick(AdapterView<?> arg0, View clickedView, - int position, long arg3) { - if (getSelectedConversation() != conversationList.get(position)) { - setSelectedConversation(conversationList.get(position)); - ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation()); - conversationWasSelectedByKeyboard = false; - } - hideConversationsOverview(); - openConversation(); - } - }); - - listView.setDismissCallback(new EnhancedListView.OnDismissCallback() { - - @Override - public EnhancedListView.Undoable onDismiss(final EnhancedListView enhancedListView, final int position) { - - final int index = listView.getFirstVisiblePosition(); - View v = listView.getChildAt(0); - final int top = (v == null) ? 0 : (v.getTop() - listView.getPaddingTop()); - - try { - swipedConversation = listAdapter.getItem(position); - } catch (IndexOutOfBoundsException e) { - return null; - } - listAdapter.remove(swipedConversation); - xmppConnectionService.markRead(swipedConversation); - - final boolean formerlySelected = (getSelectedConversation() == swipedConversation); - if (position == 0 && listAdapter.getCount() == 0) { - endConversation(swipedConversation, false, true); - return null; - } else if (formerlySelected) { - setSelectedConversation(listAdapter.getItem(0)); - ConversationActivity.this.mConversationFragment - .reInit(getSelectedConversation()); - } - - return new EnhancedListView.Undoable() { - - @Override - public void undo() { - listAdapter.insert(swipedConversation, position); - if (formerlySelected) { - setSelectedConversation(swipedConversation); - ConversationActivity.this.mConversationFragment - .reInit(getSelectedConversation()); - } - swipedConversation = null; - listView.setSelectionFromTop(index + (listView.getChildCount() < position ? 1 : 0), top); - } - - @Override - public void discard() { - if (!swipedConversation.isRead() - && swipedConversation.getMode() == Conversation.MODE_SINGLE) { - swipedConversation = null; - return; - } - endConversation(swipedConversation, false, false); - swipedConversation = null; - } - - @Override - public String getTitle() { - if (swipedConversation.getMode() == Conversation.MODE_MULTI) { - return getResources().getString(R.string.title_undo_swipe_out_muc); - } else { - return getResources().getString(R.string.title_undo_swipe_out_conversation); - } - } - }; - } - }); - listView.enableSwipeToDismiss(); - listView.setSwipingLayout(R.id.swipeable_item); - listView.setUndoStyle(EnhancedListView.UndoStyle.SINGLE_POPUP); - listView.setUndoHideDelay(5000); - listView.setRequireTouchBeforeDismiss(false); - - mContentView = findViewById(R.id.content_view_spl); - if (mContentView == null) { - mContentView = findViewById(R.id.content_view_ll); - } - if (mContentView instanceof SlidingPaneLayout) { - SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView; - mSlidingPaneLayout.setParallaxDistance(150); - mSlidingPaneLayout - .setShadowResource(R.drawable.es_slidingpane_shadow); - mSlidingPaneLayout.setSliderFadeColor(0); - mSlidingPaneLayout.setPanelSlideListener(new PanelSlideListener() { - - @Override - public void onPanelOpened(View arg0) { - updateActionBarTitle(); - invalidateOptionsMenu(); - hideKeyboard(); - if (xmppConnectionServiceBound) { - xmppConnectionService.getNotificationService() - .setOpenConversation(null); - } - closeContextMenu(); - } - - @Override - public void onPanelClosed(View arg0) { - listView.discardUndo(); - openConversation(); - } - - @Override - public void onPanelSlide(View arg0, float arg1) { - // TODO Auto-generated method stub - - } - }); - } - } - - @Override - public void switchToConversation(Conversation conversation) { - setSelectedConversation(conversation); - runOnUiThread(new Runnable() { - @Override - public void run() { - ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation()); - openConversation(); - } - }); - } - - private void updateActionBarTitle() { - updateActionBarTitle(isConversationsOverviewHideable() && !isConversationsOverviewVisable()); - } - - private void updateActionBarTitle(boolean titleShouldBeName) { - final ActionBar ab = getActionBar(); - final Conversation conversation = getSelectedConversation(); - if (ab != null) { - if (titleShouldBeName && conversation != null) { - ab.setDisplayHomeAsUpEnabled(true); - ab.setHomeButtonEnabled(true); - if (conversation.getMode() == Conversation.MODE_SINGLE || useSubjectToIdentifyConference()) { - ab.setTitle(conversation.getName()); - } else { - ab.setTitle(conversation.getJid().toBareJid().toString()); - } - } else { - ab.setDisplayHomeAsUpEnabled(false); - ab.setHomeButtonEnabled(false); - ab.setTitle(R.string.app_name); - } - } - } - - private void openConversation() { - this.updateActionBarTitle(); - this.invalidateOptionsMenu(); - if (xmppConnectionServiceBound) { - final Conversation conversation = getSelectedConversation(); - xmppConnectionService.getNotificationService().setOpenConversation(conversation); - sendReadMarkerIfNecessary(conversation); - } - listAdapter.notifyDataSetChanged(); - } - - public void sendReadMarkerIfNecessary(final Conversation conversation) { - if (!mActivityPaused && conversation != null) { - if (!conversation.isRead()) { - xmppConnectionService.sendReadMarker(conversation); - } else { - xmppConnectionService.markRead(conversation); - } - } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.conversations, menu); - final MenuItem menuSecure = menu.findItem(R.id.action_security); - final MenuItem menuArchive = menu.findItem(R.id.action_archive); - final MenuItem menuMucDetails = menu.findItem(R.id.action_muc_details); - final MenuItem menuContactDetails = menu.findItem(R.id.action_contact_details); - final MenuItem menuAttach = menu.findItem(R.id.action_attach_file); - final MenuItem menuClearHistory = menu.findItem(R.id.action_clear_history); - final MenuItem menuAdd = menu.findItem(R.id.action_add); - final MenuItem menuInviteContact = menu.findItem(R.id.action_invite); - final MenuItem menuMute = menu.findItem(R.id.action_mute); - final MenuItem menuUnmute = menu.findItem(R.id.action_unmute); - - if (isConversationsOverviewVisable() && isConversationsOverviewHideable()) { - menuArchive.setVisible(false); - menuMucDetails.setVisible(false); - menuContactDetails.setVisible(false); - menuSecure.setVisible(false); - menuInviteContact.setVisible(false); - menuAttach.setVisible(false); - menuClearHistory.setVisible(false); - menuMute.setVisible(false); - menuUnmute.setVisible(false); - } else { - menuAdd.setVisible(!isConversationsOverviewHideable()); - if (this.getSelectedConversation() != null) { - if (this.getSelectedConversation().getNextEncryption() != Message.ENCRYPTION_NONE) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - menuSecure.setIcon(R.drawable.ic_lock_white_24dp); - } else { - menuSecure.setIcon(R.drawable.ic_action_secure); - } - } - if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) { - menuContactDetails.setVisible(false); - menuAttach.setVisible(getSelectedConversation().getAccount().httpUploadAvailable() && getSelectedConversation().getMucOptions().participating()); - menuInviteContact.setVisible(getSelectedConversation().getMucOptions().canInvite()); - menuSecure.setVisible(!Config.HIDE_PGP_IN_UI); //if pgp is hidden conferences have no choice of encryption - } else { - menuMucDetails.setVisible(false); - } - if (this.getSelectedConversation().isMuted()) { - menuMute.setVisible(false); - } else { - menuUnmute.setVisible(false); - } - } - } - return true; - } - - protected void selectPresenceToAttachFile(final int attachmentChoice, final int encryption) { - final Conversation conversation = getSelectedConversation(); - final Account account = conversation.getAccount(); - final OnPresenceSelected callback = new OnPresenceSelected() { - - @Override - public void onPresenceSelected() { - Intent intent = new Intent(); - boolean chooser = false; - String fallbackPackageId = null; - switch (attachmentChoice) { - case ATTACHMENT_CHOICE_CHOOSE_IMAGE: - intent.setAction(Intent.ACTION_GET_CONTENT); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE,true); - } - intent.setType("image/*"); - chooser = true; - break; - case ATTACHMENT_CHOICE_TAKE_PHOTO: - Uri uri = xmppConnectionService.getFileBackend().getTakePhotoUri(); - intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE); - intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); - mPendingImageUris.clear(); - mPendingImageUris.add(uri); - break; - case ATTACHMENT_CHOICE_CHOOSE_FILE: - chooser = true; - intent.setType("*/*"); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setAction(Intent.ACTION_GET_CONTENT); - break; - case ATTACHMENT_CHOICE_RECORD_VOICE: - intent.setAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION); - fallbackPackageId = "eu.siacs.conversations.voicerecorder"; - break; - case ATTACHMENT_CHOICE_LOCATION: - intent.setAction("eu.siacs.conversations.location.request"); - fallbackPackageId = "eu.siacs.conversations.sharelocation"; - break; - } - if (intent.resolveActivity(getPackageManager()) != null) { - if (chooser) { - startActivityForResult( - Intent.createChooser(intent, getString(R.string.perform_action_with)), - attachmentChoice); - } else { - startActivityForResult(intent, attachmentChoice); - } - } else if (fallbackPackageId != null) { - startActivity(getInstallApkIntent(fallbackPackageId)); - } - } - }; - if ((account.httpUploadAvailable() || attachmentChoice == ATTACHMENT_CHOICE_LOCATION) && encryption != Message.ENCRYPTION_OTR) { - conversation.setNextCounterpart(null); - callback.onPresenceSelected(); - } else { - selectPresence(conversation, callback); - } - } - - private Intent getInstallApkIntent(final String packageId) { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse("market://details?id=" + packageId)); - if (intent.resolveActivity(getPackageManager()) != null) { - return intent; - } else { - intent.setData(Uri.parse("http://play.google.com/store/apps/details?id=" + packageId)); - return intent; - } - } - - public void attachFile(final int attachmentChoice) { - switch (attachmentChoice) { - case ATTACHMENT_CHOICE_LOCATION: - getPreferences().edit().putString("recently_used_quick_action","location").apply(); - break; - case ATTACHMENT_CHOICE_RECORD_VOICE: - getPreferences().edit().putString("recently_used_quick_action","voice").apply(); - break; - case ATTACHMENT_CHOICE_TAKE_PHOTO: - getPreferences().edit().putString("recently_used_quick_action","photo").apply(); - break; - case ATTACHMENT_CHOICE_CHOOSE_IMAGE: - getPreferences().edit().putString("recently_used_quick_action","picture").apply(); - break; - } - final Conversation conversation = getSelectedConversation(); - final int encryption = conversation.getNextEncryption(); - if (encryption == Message.ENCRYPTION_PGP) { - if (hasPgp()) { - if (conversation.getContact().getPgpKeyId() != 0) { - xmppConnectionService.getPgpEngine().hasKey( - conversation.getContact(), - new UiCallback<Contact>() { - - @Override - public void userInputRequried(PendingIntent pi, - Contact contact) { - ConversationActivity.this.runIntent(pi,attachmentChoice); - } - - @Override - public void success(Contact contact) { - selectPresenceToAttachFile(attachmentChoice,encryption); - } - - @Override - public void error(int error, Contact contact) { - displayErrorDialog(error); - } - }); - } else { - final ConversationFragment fragment = (ConversationFragment) getFragmentManager() - .findFragmentByTag("conversation"); - if (fragment != null) { - fragment.showNoPGPKeyDialog(false, - new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, - int which) { - conversation - .setNextEncryption(Message.ENCRYPTION_NONE); - xmppConnectionService.databaseBackend - .updateConversation(conversation); - selectPresenceToAttachFile(attachmentChoice,Message.ENCRYPTION_NONE); - } - }); - } - } - } else { - showInstallPgpDialog(); - } - } else { - if (encryption != Message.ENCRYPTION_AXOLOTL || !trustKeysIfNeeded(REQUEST_TRUST_KEYS_MENU, attachmentChoice)) { - selectPresenceToAttachFile(attachmentChoice, encryption); - } - } - } - - @Override - public boolean onOptionsItemSelected(final MenuItem item) { - if (item.getItemId() == android.R.id.home) { - showConversationsOverview(); - return true; - } else if (item.getItemId() == R.id.action_add) { - startActivity(new Intent(this, StartConversationActivity.class)); - return true; - } else if (getSelectedConversation() != null) { - switch (item.getItemId()) { - case R.id.action_attach_file: - attachFileDialog(); - break; - case R.id.action_archive: - this.endConversation(getSelectedConversation()); - break; - case R.id.action_contact_details: - switchToContactDetails(getSelectedConversation().getContact()); - break; - case R.id.action_muc_details: - Intent intent = new Intent(this, - ConferenceDetailsActivity.class); - intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC); - intent.putExtra("uuid", getSelectedConversation().getUuid()); - startActivity(intent); - break; - case R.id.action_invite: - inviteToConversation(getSelectedConversation()); - break; - case R.id.action_security: - selectEncryptionDialog(getSelectedConversation()); - break; - case R.id.action_clear_history: - clearHistoryDialog(getSelectedConversation()); - break; - case R.id.action_mute: - muteConversationDialog(getSelectedConversation()); - break; - case R.id.action_unmute: - unmuteConversation(getSelectedConversation()); - break; - case R.id.action_block: - BlockContactDialog.show(this, xmppConnectionService, getSelectedConversation()); - break; - case R.id.action_unblock: - BlockContactDialog.show(this, xmppConnectionService, getSelectedConversation()); - break; - default: - break; - } - return super.onOptionsItemSelected(item); - } else { - return super.onOptionsItemSelected(item); - } - } - - public void endConversation(Conversation conversation) { - endConversation(conversation, true, true); - } - - public void endConversation(Conversation conversation, boolean showOverview, boolean reinit) { - if (showOverview) { - showConversationsOverview(); - } - xmppConnectionService.archiveConversation(conversation); - if (reinit) { - if (conversationList.size() > 0) { - setSelectedConversation(conversationList.get(0)); - this.mConversationFragment.reInit(getSelectedConversation()); - } else { - setSelectedConversation(null); - if (mRedirected.compareAndSet(false,true)) { - Intent intent = new Intent(this, StartConversationActivity.class); - intent.putExtra("init",true); - startActivity(intent); - finish(); - } - } - } - } - - @SuppressLint("InflateParams") - protected void clearHistoryDialog(final Conversation conversation) { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(getString(R.string.clear_conversation_history)); - View dialogView = getLayoutInflater().inflate( - R.layout.dialog_clear_history, null); - final CheckBox endConversationCheckBox = (CheckBox) dialogView - .findViewById(R.id.end_conversation_checkbox); - builder.setView(dialogView); - builder.setNegativeButton(getString(R.string.cancel), null); - builder.setPositiveButton(getString(R.string.delete_messages), - new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - ConversationActivity.this.xmppConnectionService.clearConversationHistory(conversation); - if (endConversationCheckBox.isChecked()) { - endConversation(conversation); - } else { - updateConversationList(); - ConversationActivity.this.mConversationFragment.updateMessages(); - } - } - }); - builder.create().show(); - } - - protected void attachFileDialog() { - View menuAttachFile = findViewById(R.id.action_attach_file); - if (menuAttachFile == null) { - return; - } - PopupMenu attachFilePopup = new PopupMenu(this, menuAttachFile); - attachFilePopup.inflate(R.menu.attachment_choices); - if (new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION).resolveActivity(getPackageManager()) == null) { - attachFilePopup.getMenu().findItem(R.id.attach_record_voice).setVisible(false); - } - if (new Intent("eu.siacs.conversations.location.request").resolveActivity(getPackageManager()) == null) { - attachFilePopup.getMenu().findItem(R.id.attach_location).setVisible(false); - } - attachFilePopup.setOnMenuItemClickListener(new OnMenuItemClickListener() { - - @Override - public boolean onMenuItemClick(MenuItem item) { - switch (item.getItemId()) { - case R.id.attach_choose_picture: - attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE); - break; - case R.id.attach_take_picture: - attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO); - break; - case R.id.attach_choose_file: - attachFile(ATTACHMENT_CHOICE_CHOOSE_FILE); - break; - case R.id.attach_record_voice: - attachFile(ATTACHMENT_CHOICE_RECORD_VOICE); - break; - case R.id.attach_location: - attachFile(ATTACHMENT_CHOICE_LOCATION); - break; - } - return false; - } - }); - attachFilePopup.show(); - } - - public void verifyOtrSessionDialog(final Conversation conversation, View view) { - if (!conversation.hasValidOtrSession() || conversation.getOtrSession().getSessionStatus() != SessionStatus.ENCRYPTED) { - Toast.makeText(this, R.string.otr_session_not_started, Toast.LENGTH_LONG).show(); - return; - } - if (view == null) { - return; - } - PopupMenu popup = new PopupMenu(this, view); - popup.inflate(R.menu.verification_choices); - popup.setOnMenuItemClickListener(new OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem menuItem) { - Intent intent = new Intent(ConversationActivity.this, VerifyOTRActivity.class); - intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT); - intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString()); - intent.putExtra("account", conversation.getAccount().getJid().toBareJid().toString()); - switch (menuItem.getItemId()) { - case R.id.scan_fingerprint: - intent.putExtra("mode", VerifyOTRActivity.MODE_SCAN_FINGERPRINT); - break; - case R.id.ask_question: - intent.putExtra("mode", VerifyOTRActivity.MODE_ASK_QUESTION); - break; - case R.id.manual_verification: - intent.putExtra("mode", VerifyOTRActivity.MODE_MANUAL_VERIFICATION); - break; - } - startActivity(intent); - return true; - } - }); - popup.show(); - } - - protected void selectEncryptionDialog(final Conversation conversation) { - View menuItemView = findViewById(R.id.action_security); - if (menuItemView == null) { - return; - } - PopupMenu popup = new PopupMenu(this, menuItemView); - final ConversationFragment fragment = (ConversationFragment) getFragmentManager() - .findFragmentByTag("conversation"); - if (fragment != null) { - popup.setOnMenuItemClickListener(new OnMenuItemClickListener() { - - @Override - public boolean onMenuItemClick(MenuItem item) { - switch (item.getItemId()) { - case R.id.encryption_choice_none: - conversation.setNextEncryption(Message.ENCRYPTION_NONE); - item.setChecked(true); - break; - case R.id.encryption_choice_otr: - conversation.setNextEncryption(Message.ENCRYPTION_OTR); - item.setChecked(true); - break; - case R.id.encryption_choice_pgp: - if (hasPgp()) { - if (conversation.getAccount().getKeys().has("pgp_signature")) { - conversation.setNextEncryption(Message.ENCRYPTION_PGP); - item.setChecked(true); - } else { - announcePgp(conversation.getAccount(),conversation); - } - } else { - showInstallPgpDialog(); - } - break; - case R.id.encryption_choice_axolotl: - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(conversation.getAccount()) - + "Enabled axolotl for Contact " + conversation.getContact().getJid()); - conversation.setNextEncryption(Message.ENCRYPTION_AXOLOTL); - item.setChecked(true); - break; - default: - conversation.setNextEncryption(Message.ENCRYPTION_NONE); - break; - } - xmppConnectionService.databaseBackend.updateConversation(conversation); - fragment.updateChatMsgHint(); - invalidateOptionsMenu(); - refreshUi(); - return true; - } - }); - popup.inflate(R.menu.encryption_choices); - MenuItem otr = popup.getMenu().findItem(R.id.encryption_choice_otr); - MenuItem none = popup.getMenu().findItem(R.id.encryption_choice_none); - MenuItem pgp = popup.getMenu().findItem(R.id.encryption_choice_pgp); - MenuItem axolotl = popup.getMenu().findItem(R.id.encryption_choice_axolotl); - pgp.setVisible(!Config.HIDE_PGP_IN_UI); - if (conversation.getMode() == Conversation.MODE_MULTI) { - otr.setVisible(false); - axolotl.setVisible(false); - } else if (!conversation.getAccount().getAxolotlService().isContactAxolotlCapable(conversation.getContact())) { - axolotl.setEnabled(false); - } - switch (conversation.getNextEncryption()) { - case Message.ENCRYPTION_NONE: - none.setChecked(true); - break; - case Message.ENCRYPTION_OTR: - otr.setChecked(true); - break; - case Message.ENCRYPTION_PGP: - pgp.setChecked(true); - break; - case Message.ENCRYPTION_AXOLOTL: - axolotl.setChecked(true); - break; - default: - none.setChecked(true); - break; - } - popup.show(); - } - } - - protected void muteConversationDialog(final Conversation conversation) { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.disable_notifications); - final int[] durations = getResources().getIntArray(R.array.mute_options_durations); - builder.setItems(R.array.mute_options_descriptions, - new OnClickListener() { - - @Override - public void onClick(final DialogInterface dialog, final int which) { - final long till; - if (durations[which] == -1) { - till = Long.MAX_VALUE; - } else { - till = System.currentTimeMillis() + (durations[which] * 1000); - } - conversation.setMutedTill(till); - ConversationActivity.this.xmppConnectionService.databaseBackend - .updateConversation(conversation); - updateConversationList(); - ConversationActivity.this.mConversationFragment.updateMessages(); - invalidateOptionsMenu(); - } - }); - builder.create().show(); - } - - public void unmuteConversation(final Conversation conversation) { - conversation.setMutedTill(0); - this.xmppConnectionService.databaseBackend.updateConversation(conversation); - updateConversationList(); - ConversationActivity.this.mConversationFragment.updateMessages(); - invalidateOptionsMenu(); - } - - @Override - public void onBackPressed() { - if (!isConversationsOverviewVisable()) { - showConversationsOverview(); - } else { - moveTaskToBack(true); - } - } - - @Override - public boolean onKeyUp(int key, KeyEvent event) { - int rotation = getWindowManager().getDefaultDisplay().getRotation(); - final int upKey; - final int downKey; - switch(rotation) { - case Surface.ROTATION_90: - upKey = KeyEvent.KEYCODE_DPAD_LEFT; - downKey = KeyEvent.KEYCODE_DPAD_RIGHT; - break; - case Surface.ROTATION_180: - upKey = KeyEvent.KEYCODE_DPAD_DOWN; - downKey = KeyEvent.KEYCODE_DPAD_UP; - break; - case Surface.ROTATION_270: - upKey = KeyEvent.KEYCODE_DPAD_RIGHT; - downKey = KeyEvent.KEYCODE_DPAD_LEFT; - break; - default: - upKey = KeyEvent.KEYCODE_DPAD_UP; - downKey = KeyEvent.KEYCODE_DPAD_DOWN; - } - final boolean modifier = event.isCtrlPressed() || event.isAltPressed(); - if (modifier && key == KeyEvent.KEYCODE_TAB && isConversationsOverviewHideable()) { - toggleConversationsOverview(); - return true; - } else if (modifier && key == downKey) { - if (isConversationsOverviewHideable() && !isConversationsOverviewVisable()) { - showConversationsOverview();; - } - return selectDownConversation(); - } else if (modifier && key == upKey) { - if (isConversationsOverviewHideable() && !isConversationsOverviewVisable()) { - showConversationsOverview(); - } - return selectUpConversation(); - } else if (modifier && key == KeyEvent.KEYCODE_1) { - return openConversationByIndex(0); - } else if (modifier && key == KeyEvent.KEYCODE_2) { - return openConversationByIndex(1); - } else if (modifier && key == KeyEvent.KEYCODE_3) { - return openConversationByIndex(2); - } else if (modifier && key == KeyEvent.KEYCODE_4) { - return openConversationByIndex(3); - } else if (modifier && key == KeyEvent.KEYCODE_5) { - return openConversationByIndex(4); - } else if (modifier && key == KeyEvent.KEYCODE_6) { - return openConversationByIndex(5); - } else if (modifier && key == KeyEvent.KEYCODE_7) { - return openConversationByIndex(6); - } else if (modifier && key == KeyEvent.KEYCODE_8) { - return openConversationByIndex(7); - } else if (modifier && key == KeyEvent.KEYCODE_9) { - return openConversationByIndex(8); - } else if (modifier && key == KeyEvent.KEYCODE_0) { - return openConversationByIndex(9); - } else { - return super.onKeyUp(key, event); - } - } - - private void toggleConversationsOverview() { - if (isConversationsOverviewVisable()) { - hideConversationsOverview(); - if (mConversationFragment != null) { - mConversationFragment.setFocusOnInputField(); - } - } else { - showConversationsOverview(); - } - } - - private boolean selectUpConversation() { - if (this.mSelectedConversation != null) { - int index = this.conversationList.indexOf(this.mSelectedConversation); - if (index > 0) { - return openConversationByIndex(index - 1); - } - } - return false; - } - - private boolean selectDownConversation() { - if (this.mSelectedConversation != null) { - int index = this.conversationList.indexOf(this.mSelectedConversation); - if (index != -1 && index < this.conversationList.size() - 1) { - return openConversationByIndex(index + 1); - } - } - return false; - } - - private boolean openConversationByIndex(int index) { - try { - this.conversationWasSelectedByKeyboard = true; - setSelectedConversation(this.conversationList.get(index)); - this.mConversationFragment.reInit(getSelectedConversation()); - if (index > listView.getLastVisiblePosition() - 1 || index < listView.getFirstVisiblePosition() + 1) { - this.listView.setSelection(index); - } - openConversation(); - return true; - } catch (IndexOutOfBoundsException e) { - return false; - } - } - - @Override - protected void onNewIntent(final Intent intent) { - if (xmppConnectionServiceBound) { - if (intent != null && VIEW_CONVERSATION.equals(intent.getType())) { - handleViewConversationIntent(intent); - } - } else { - setIntent(intent); - } - } - - @Override - public void onStart() { - super.onStart(); - this.mRedirected.set(false); - if (this.xmppConnectionServiceBound) { - this.onBackendConnected(); - } - if (conversationList.size() >= 1) { - this.onConversationUpdate(); - } - } - - @Override - public void onPause() { - listView.discardUndo(); - super.onPause(); - this.mActivityPaused = true; - if (this.xmppConnectionServiceBound) { - this.xmppConnectionService.getNotificationService().setIsInForeground(false); - } - } - - @Override - public void onResume() { - super.onResume(); - final int theme = findTheme(); - final boolean usingEnterKey = usingEnterKey(); - if (this.mTheme != theme || usingEnterKey != mUsingEnterKey) { - recreate(); - } - this.mActivityPaused = false; - if (this.xmppConnectionServiceBound) { - this.xmppConnectionService.getNotificationService().setIsInForeground(true); - } - - if (!isConversationsOverviewVisable() || !isConversationsOverviewHideable()) { - sendReadMarkerIfNecessary(getSelectedConversation()); - } - - } - - @Override - public void onSaveInstanceState(final Bundle savedInstanceState) { - Conversation conversation = getSelectedConversation(); - if (conversation != null) { - savedInstanceState.putString(STATE_OPEN_CONVERSATION, - conversation.getUuid()); - } - savedInstanceState.putBoolean(STATE_PANEL_OPEN, - isConversationsOverviewVisable()); - if (this.mPendingImageUris.size() >= 1) { - savedInstanceState.putString(STATE_PENDING_URI, this.mPendingImageUris.get(0).toString()); - } - super.onSaveInstanceState(savedInstanceState); - } - - @Override - void onBackendConnected() { - this.xmppConnectionService.getNotificationService().setIsInForeground(true); - updateConversationList(); - - if (mPendingConferenceInvite != null) { - mPendingConferenceInvite.execute(this); - mPendingConferenceInvite = null; - } - - if (xmppConnectionService.getAccounts().size() == 0) { - if (mRedirected.compareAndSet(false,true)) { - if (Config.X509_VERIFICATION) { - startActivity(new Intent(this, ManageAccountActivity.class)); - } else { - startActivity(new Intent(this, EditAccountActivity.class)); - } - finish(); - } - } else if (conversationList.size() <= 0) { - if (mRedirected.compareAndSet(false,true)) { - Intent intent = new Intent(this, StartConversationActivity.class); - intent.putExtra("init",true); - startActivity(intent); - finish(); - } - } else if (getIntent() != null && VIEW_CONVERSATION.equals(getIntent().getType())) { - handleViewConversationIntent(getIntent()); - } else if (selectConversationByUuid(mOpenConverstaion)) { - if (mPanelOpen) { - showConversationsOverview(); - } else { - if (isConversationsOverviewHideable()) { - openConversation(); - } - } - this.mConversationFragment.reInit(getSelectedConversation()); - mOpenConverstaion = null; - } else if (getSelectedConversation() == null) { - showConversationsOverview(); - mPendingImageUris.clear(); - mPendingFileUris.clear(); - mPendingGeoUri = null; - setSelectedConversation(conversationList.get(0)); - this.mConversationFragment.reInit(getSelectedConversation()); - } else { - this.mConversationFragment.messageListAdapter.updatePreferences(); - this.mConversationFragment.messagesView.invalidateViews(); - this.mConversationFragment.setupIme(); - } - - if(!forbidProcessingPendings) { - for (Iterator<Uri> i = mPendingImageUris.iterator(); i.hasNext(); i.remove()) { - Uri foo = i.next(); - attachImageToConversation(getSelectedConversation(), foo); - } - - for (Iterator<Uri> i = mPendingFileUris.iterator(); i.hasNext(); i.remove()) { - attachFileToConversation(getSelectedConversation(), i.next()); - } - - if (mPendingGeoUri != null) { - attachLocationToConversation(getSelectedConversation(), mPendingGeoUri); - mPendingGeoUri = null; - } - } - forbidProcessingPendings = false; - - ExceptionHelper.checkForCrash(this, this.xmppConnectionService); - setIntent(new Intent()); - } - - private void handleViewConversationIntent(final Intent intent) { - final String uuid = intent.getStringExtra(CONVERSATION); - final String downloadUuid = intent.getStringExtra(MESSAGE); - final String text = intent.getStringExtra(TEXT); - final String nick = intent.getStringExtra(NICK); - final boolean pm = intent.getBooleanExtra(PRIVATE_MESSAGE,false); - if (selectConversationByUuid(uuid)) { - this.mConversationFragment.reInit(getSelectedConversation()); - if (nick != null) { - if (pm) { - Jid jid = getSelectedConversation().getJid(); - try { - Jid next = Jid.fromParts(jid.getLocalpart(),jid.getDomainpart(),nick); - this.mConversationFragment.privateMessageWith(next); - } catch (final InvalidJidException ignored) { - //do nothing - } - } else { - this.mConversationFragment.highlightInConference(nick); - } - } else { - this.mConversationFragment.appendText(text); - } - hideConversationsOverview(); - openConversation(); - if (mContentView instanceof SlidingPaneLayout) { - updateActionBarTitle(true); //fixes bug where slp isn't properly closed yet - } - if (downloadUuid != null) { - final Message message = mSelectedConversation.findMessageWithFileAndUuid(downloadUuid); - if (message != null) { - mConversationFragment.messageListAdapter.startDownloadable(message); - } - } - } - } - - private boolean selectConversationByUuid(String uuid) { - if (uuid == null) { - return false; - } - for (Conversation aConversationList : conversationList) { - if (aConversationList.getUuid().equals(uuid)) { - setSelectedConversation(aConversationList); - return true; - } - } - return false; - } - - @Override - protected void unregisterListeners() { - super.unregisterListeners(); - xmppConnectionService.getNotificationService().setOpenConversation(null); - } - - @SuppressLint("NewApi") - private static List<Uri> extractUriFromIntent(final Intent intent) { - List<Uri> uris = new ArrayList<>(); - Uri uri = intent.getData(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && uri == null) { - ClipData clipData = intent.getClipData(); - for(int i = 0; i < clipData.getItemCount(); ++i) { - uris.add(clipData.getItemAt(i).getUri()); - } - } else { - uris.add(uri); - } - return uris; - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, - final Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (resultCode == RESULT_OK) { - if (requestCode == REQUEST_DECRYPT_PGP) { - mConversationFragment.onActivityResult(requestCode, resultCode, data); - } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) { - mPendingImageUris.clear(); - mPendingImageUris.addAll(extractUriFromIntent(data)); - if (xmppConnectionServiceBound) { - for(Iterator<Uri> i = mPendingImageUris.iterator(); i.hasNext(); i.remove()) { - attachImageToConversation(getSelectedConversation(),i.next()); - } - } - } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_FILE || requestCode == ATTACHMENT_CHOICE_RECORD_VOICE) { - mPendingFileUris.clear(); - mPendingFileUris.addAll(extractUriFromIntent(data)); - if (xmppConnectionServiceBound) { - for(Iterator<Uri> i = mPendingFileUris.iterator(); i.hasNext(); i.remove()) { - attachFileToConversation(getSelectedConversation(), i.next()); - } - } - } else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) { - if (mPendingImageUris.size() == 1) { - Uri uri = mPendingImageUris.get(0); - if (xmppConnectionServiceBound) { - attachImageToConversation(getSelectedConversation(), uri); - mPendingImageUris.clear(); - } - Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); - intent.setData(uri); - sendBroadcast(intent); - } else { - mPendingImageUris.clear(); - } - } else if (requestCode == ATTACHMENT_CHOICE_LOCATION) { - double latitude = data.getDoubleExtra("latitude",0); - double longitude = data.getDoubleExtra("longitude",0); - this.mPendingGeoUri = Uri.parse("geo:"+String.valueOf(latitude)+","+String.valueOf(longitude)); - if (xmppConnectionServiceBound) { - attachLocationToConversation(getSelectedConversation(), mPendingGeoUri); - this.mPendingGeoUri = null; - } - } else if (requestCode == REQUEST_TRUST_KEYS_TEXT || requestCode == REQUEST_TRUST_KEYS_MENU) { - this.forbidProcessingPendings = !xmppConnectionServiceBound; - mConversationFragment.onActivityResult(requestCode, resultCode, data); - } - } else { - mPendingImageUris.clear(); - mPendingFileUris.clear(); - if (requestCode == ConversationActivity.REQUEST_DECRYPT_PGP) { - mConversationFragment.onActivityResult(requestCode, resultCode, data); - } - } - } - - private void attachLocationToConversation(Conversation conversation, Uri uri) { - if (conversation == null) { - return; - } - xmppConnectionService.attachLocationToConversation(conversation,uri, new UiCallback<Message>() { - - @Override - public void success(Message message) { - xmppConnectionService.sendMessage(message); - } - - @Override - public void error(int errorCode, Message object) { - - } - - @Override - public void userInputRequried(PendingIntent pi, Message object) { - - } - }); - } - - private void attachFileToConversation(Conversation conversation, Uri uri) { - if (conversation == null) { - return; - } - prepareFileToast = Toast.makeText(getApplicationContext(),getText(R.string.preparing_file), Toast.LENGTH_LONG); - prepareFileToast.show(); - xmppConnectionService.attachFileToConversation(conversation, uri, new UiCallback<Message>() { - @Override - public void success(Message message) { - hidePrepareFileToast(); - xmppConnectionService.sendMessage(message); - } - - @Override - public void error(int errorCode, Message message) { - displayErrorDialog(errorCode); - } - - @Override - public void userInputRequried(PendingIntent pi, Message message) { - - } - }); - } - - private void attachImageToConversation(Conversation conversation, Uri uri) { - if (conversation == null) { - return; - } - prepareFileToast = Toast.makeText(getApplicationContext(),getText(R.string.preparing_image), Toast.LENGTH_LONG); - prepareFileToast.show(); - xmppConnectionService.attachImageToConversation(conversation, uri, - new UiCallback<Message>() { - - @Override - public void userInputRequried(PendingIntent pi, - Message object) { - hidePrepareFileToast(); - } - - @Override - public void success(Message message) { - xmppConnectionService.sendMessage(message); - } - - @Override - public void error(int error, Message message) { - hidePrepareFileToast(); - displayErrorDialog(error); - } - }); - } - - private void hidePrepareFileToast() { - if (prepareFileToast != null) { - runOnUiThread(new Runnable() { - - @Override - public void run() { - prepareFileToast.cancel(); - } - }); - } - } - - public void updateConversationList() { - xmppConnectionService - .populateWithOrderedConversations(conversationList); - if (swipedConversation != null) { - if (swipedConversation.isRead()) { - conversationList.remove(swipedConversation); - } else { - listView.discardUndo(); - } - } - listAdapter.notifyDataSetChanged(); - } - - public void runIntent(PendingIntent pi, int requestCode) { - try { - this.startIntentSenderForResult(pi.getIntentSender(), requestCode, - null, 0, 0, 0); - } catch (final SendIntentException ignored) { - } - } - - public void encryptTextMessage(Message message) { - xmppConnectionService.getPgpEngine().encrypt(message, - new UiCallback<Message>() { - - @Override - public void userInputRequried(PendingIntent pi, - Message message) { - ConversationActivity.this.runIntent(pi, - ConversationActivity.REQUEST_SEND_MESSAGE); - } - - @Override - public void success(Message message) { - message.setEncryption(Message.ENCRYPTION_DECRYPTED); - xmppConnectionService.sendMessage(message); - } - - @Override - public void error(int error, Message message) { - - } - }); - } - - public boolean useSendButtonToIndicateStatus() { - return getPreferences().getBoolean("send_button_status", false); - } - - public boolean indicateReceived() { - return getPreferences().getBoolean("indicate_received", false); - } - - public boolean useWhiteBackground() { - return getPreferences().getBoolean("use_white_background",false); - } - - protected boolean trustKeysIfNeeded(int requestCode) { - return trustKeysIfNeeded(requestCode, ATTACHMENT_CHOICE_INVALID); - } - - protected boolean trustKeysIfNeeded(int requestCode, int attachmentChoice) { - AxolotlService axolotlService = mSelectedConversation.getAccount().getAxolotlService(); - boolean hasPendingKeys = !axolotlService.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED, - mSelectedConversation.getContact()).isEmpty() - || !axolotlService.findDevicesWithoutSession(mSelectedConversation).isEmpty(); - boolean hasNoTrustedKeys = axolotlService.getNumTrustedKeys(mSelectedConversation.getContact()) == 0; - if( hasPendingKeys || hasNoTrustedKeys) { - axolotlService.createSessionsIfNeeded(mSelectedConversation); - Intent intent = new Intent(getApplicationContext(), TrustKeysActivity.class); - intent.putExtra("contact", mSelectedConversation.getContact().getJid().toBareJid().toString()); - intent.putExtra("account", mSelectedConversation.getAccount().getJid().toBareJid().toString()); - intent.putExtra("choice", attachmentChoice); - intent.putExtra("has_no_trusted", hasNoTrustedKeys); - startActivityForResult(intent, requestCode); - return true; - } else { - return false; - } - } - - @Override - protected void refreshUiReal() { - updateConversationList(); - if (conversationList.size() > 0) { - ConversationActivity.this.mConversationFragment.updateMessages(); - updateActionBarTitle(); - invalidateOptionsMenu(); - } - } - - @Override - public void onAccountUpdate() { - this.refreshUi(); - } - - @Override - public void onConversationUpdate() { - this.refreshUi(); - } - - @Override - public void onRosterUpdate() { - this.refreshUi(); - } - - @Override - public void OnUpdateBlocklist(Status status) { - this.refreshUi(); - } - - public void unblockConversation(final Blockable conversation) { - xmppConnectionService.sendUnblockRequest(conversation); - } - - public boolean enterIsSend() { - return getPreferences().getBoolean("enter_is_send",false); - } - - @Override - public void onShowErrorToast(final int resId) { - runOnUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(ConversationActivity.this,resId,Toast.LENGTH_SHORT).show(); - } - }); - } - - public boolean highlightSelectedConversations() { - return !isConversationsOverviewHideable() || this.conversationWasSelectedByKeyboard; - } +implements OnAccountUpdate, OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist, XmppConnectionService.OnShowErrorToast { + + public static final String ACTION_DOWNLOAD = "eu.siacs.conversations.action.DOWNLOAD"; + + public static final String VIEW_CONVERSATION = "viewConversation"; + public static final String CONVERSATION = "conversationUuid"; + public static final String MESSAGE = "messageUuid"; + public static final String TEXT = "text"; + public static final String NICK = "nick"; + public static final String PRIVATE_MESSAGE = "pm"; + + public static final int REQUEST_SEND_MESSAGE = 0x0201; + public static final int REQUEST_DECRYPT_PGP = 0x0202; + public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207; + public static final int REQUEST_TRUST_KEYS_TEXT = 0x0208; + public static final int REQUEST_TRUST_KEYS_MENU = 0x0209; + public static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301; + public static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302; + public static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303; + public static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0304; + public static final int ATTACHMENT_CHOICE_LOCATION = 0x0305; + public static final int ATTACHMENT_CHOICE_INVALID = 0x0306; + private static final String STATE_OPEN_CONVERSATION = "state_open_conversation"; + private static final String STATE_PANEL_OPEN = "state_panel_open"; + private static final String STATE_PENDING_URI = "state_pending_uri"; + + private String mOpenConverstaion = null; + private boolean mPanelOpen = true; + final private List<Uri> mPendingImageUris = new ArrayList<>(); + final private List<Uri> mPendingFileUris = new ArrayList<>(); + private Uri mPendingGeoUri = null; + private boolean forbidProcessingPendings = false; + + private boolean conversationWasSelectedByKeyboard = false; + + private View mContentView; + + private List<Conversation> conversationList = new ArrayList<>(); + private Conversation swipedConversation = null; + private Conversation mSelectedConversation = null; + private EnhancedListView listView; + private ConversationFragment mConversationFragment; + + private ArrayAdapter<Conversation> listAdapter; + + private Toast prepareFileToast; + + private boolean mActivityPaused = false; + private AtomicBoolean mRedirected = new AtomicBoolean(false); + + public Conversation getSelectedConversation() { + return this.mSelectedConversation; + } + + public void setSelectedConversation(Conversation conversation) { + this.mSelectedConversation = conversation; + } + + public void showConversationsOverview() { + if (mContentView instanceof SlidingPaneLayout) { + SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView; + mSlidingPaneLayout.openPane(); + } + } + + @Override + protected String getShareableUri() { + Conversation conversation = getSelectedConversation(); + if (conversation != null) { + return conversation.getAccount().getShareableUri(); + } else { + return ""; + } + } + + public void hideConversationsOverview() { + if (mContentView instanceof SlidingPaneLayout) { + SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView; + mSlidingPaneLayout.closePane(); + } + } + + public boolean isConversationsOverviewHideable() { + if (mContentView instanceof SlidingPaneLayout) { + SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView; + return mSlidingPaneLayout.isSlideable(); + } else { + return false; + } + } + + public boolean isConversationsOverviewVisable() { + if (mContentView instanceof SlidingPaneLayout) { + SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView; + return mSlidingPaneLayout.isOpen(); + } else { + return true; + } + } + + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (savedInstanceState != null) { + mOpenConverstaion = savedInstanceState.getString(STATE_OPEN_CONVERSATION, null); + mPanelOpen = savedInstanceState.getBoolean(STATE_PANEL_OPEN, true); + String pending = savedInstanceState.getString(STATE_PENDING_URI, null); + if (pending != null) { + mPendingImageUris.clear(); + mPendingImageUris.add(Uri.parse(pending)); + } + } + + AppUpdate(); + + setContentView(R.layout.fragment_conversations_overview); + + this.mConversationFragment = new ConversationFragment(); + FragmentTransaction transaction = getFragmentManager().beginTransaction(); + transaction.replace(R.id.selected_conversation, this.mConversationFragment, "conversation"); + transaction.commit(); + + listView = (EnhancedListView) findViewById(R.id.list); + this.listAdapter = new ConversationAdapter(this, conversationList); + listView.setAdapter(this.listAdapter); + + if (getActionBar() != null) { + getActionBar().setDisplayHomeAsUpEnabled(false); + getActionBar().setHomeButtonEnabled(false); + } + + listView.setOnItemClickListener(new OnItemClickListener() { + + @Override + public void onItemClick(AdapterView<?> arg0, View clickedView, + int position, long arg3) { + if (getSelectedConversation() != conversationList.get(position)) { + setSelectedConversation(conversationList.get(position)); + ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation()); + conversationWasSelectedByKeyboard = false; + } + hideConversationsOverview(); + openConversation(); + } + }); + + listView.setDismissCallback(new EnhancedListView.OnDismissCallback() { + @Override + public EnhancedListView.Undoable onDismiss(final EnhancedListView enhancedListView, final int position) { + + final int index = listView.getFirstVisiblePosition(); + View v = listView.getChildAt(0); + final int top = (v == null) ? 0 : (v.getTop() - listView.getPaddingTop()); + + try { + swipedConversation = listAdapter.getItem(position); + } catch (IndexOutOfBoundsException e) { + return null; + } + listAdapter.remove(swipedConversation); + xmppConnectionService.markRead(swipedConversation); + + final boolean formerlySelected = (getSelectedConversation() == swipedConversation); + if (position == 0 && listAdapter.getCount() == 0) { + endConversation(swipedConversation, false, true); + return null; + } else if (formerlySelected) { + setSelectedConversation(listAdapter.getItem(0)); + ConversationActivity.this.mConversationFragment + .reInit(getSelectedConversation()); + } + + return new EnhancedListView.Undoable() { + + @Override + public void undo() { + listAdapter.insert(swipedConversation, position); + if (formerlySelected) { + setSelectedConversation(swipedConversation); + ConversationActivity.this.mConversationFragment + .reInit(getSelectedConversation()); + } + swipedConversation = null; + listView.setSelectionFromTop(index + (listView.getChildCount() < position ? 1 : 0), top); + } + + @Override + public void discard() { + if (!swipedConversation.isRead() + && swipedConversation.getMode() == Conversation.MODE_SINGLE) { + swipedConversation = null; + return; + } + endConversation(swipedConversation, false, false); + swipedConversation = null; + } + + @Override + public String getTitle() { + if (swipedConversation.getMode() == Conversation.MODE_MULTI) { + return getResources().getString(R.string.title_undo_swipe_out_muc); + } else { + return getResources().getString(R.string.title_undo_swipe_out_conversation); + } + } + }; + } + }); + listView.enableSwipeToDismiss(); + listView.setSwipingLayout(R.id.swipeable_item); + listView.setUndoStyle(EnhancedListView.UndoStyle.SINGLE_POPUP); + listView.setUndoHideDelay(10000); + listView.setRequireTouchBeforeDismiss(false); + listView.setSwipeDirection(EnhancedListView.SwipeDirection.START); // swipe to left to close conversation + + mContentView = findViewById(R.id.content_view_spl); + if (mContentView == null) { + mContentView = findViewById(R.id.content_view_ll); + } + if (mContentView instanceof SlidingPaneLayout) { + SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView; + mSlidingPaneLayout.setParallaxDistance(150); + mSlidingPaneLayout + .setShadowResource(R.drawable.es_slidingpane_shadow); + mSlidingPaneLayout.setSliderFadeColor(0); + mSlidingPaneLayout.setPanelSlideListener(new PanelSlideListener() { + + @Override + public void onPanelOpened(View arg0) { + updateActionBarTitle(); + invalidateOptionsMenu(); + hideKeyboard(); + if (xmppConnectionServiceBound) { + xmppConnectionService.getNotificationService() + .setOpenConversation(null); + } + closeContextMenu(); + } + + @Override + public void onPanelClosed(View arg0) { + listView.discardUndo(); + openConversation(); + } + + @Override + public void onPanelSlide(View arg0, float arg1) { + // TODO Auto-generated method stub + + } + }); + } + } + + protected void AppUpdate() { + String PREFS_NAME = "UpdateTimeStamp"; + SharedPreferences UpdateTimeStamp = getApplicationContext().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); + long lastUpdateTime = UpdateTimeStamp.getLong("lastUpdateTime", 0); + + Log.d(Config.LOGTAG, "AppUpdater - LastUpdateTime: " + lastUpdateTime); + + if ((lastUpdateTime + (Config.UPDATE_CHECK_TIMER * 1000)) < System.currentTimeMillis()) { + lastUpdateTime = System.currentTimeMillis(); + SharedPreferences.Editor editor = UpdateTimeStamp.edit(); + editor.putLong("lastUpdateTime", lastUpdateTime); + editor.commit(); + + // run AppUpdater + Log.d(Config.LOGTAG, "AppUpdater - CurrentTime: " + lastUpdateTime); + Intent AppUpdater = new Intent(this, UpdaterActivity.class); + startActivity(AppUpdater); + Log.d(Config.LOGTAG, "AppUpdater started"); + + } else { + + Log.d(Config.LOGTAG, "AppUpdater stopped"); + return; + } + } + + @Override + public void switchToConversation(Conversation conversation) { + setSelectedConversation(conversation); + runOnUiThread(new Runnable() { + @Override + public void run() { + ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation()); + openConversation(); + } + }); + } + + private void updateActionBarTitle() { + updateActionBarTitle(isConversationsOverviewHideable() && !isConversationsOverviewVisable()); + } + + private void updateActionBarTitle(boolean titleShouldBeName) { + final ActionBar ab = getActionBar(); + final Conversation conversation = getSelectedConversation(); + if (ab != null) { + if (titleShouldBeName && conversation != null) { + ab.setDisplayHomeAsUpEnabled(true); + ab.setHomeButtonEnabled(true); + if (conversation.getMode() == Conversation.MODE_SINGLE || useSubjectToIdentifyConference()) { + ab.setTitle(conversation.getName()); + if (conversation.getMode() == Conversation.MODE_SINGLE) { + if (conversation.getContact().getPresences().getMostAvailableStatus() == Presences.OFFLINE) { + ab.setSubtitle(getString(R.string.account_status_offline)); + } else { + ChatState state = conversation.getIncomingChatState(); + if (state == ChatState.COMPOSING) { + ab.setSubtitle(getString(R.string.is_typing)); + } else if (state == ChatState.PAUSED) { + ab.setSubtitle(UIHelper.lastseen(getApplicationContext(), conversation.getContact().lastseen.time)); + } else { + ab.setSubtitle(UIHelper.lastseen(getApplicationContext(), conversation.getContact().lastseen.time)); + } + } + } else if (useSubjectToIdentifyConference()) { + ab.setSubtitle(conversation.getParticipants()); + } + } else { + ab.setTitle(conversation.getJid().toBareJid().toString()); + ab.setSubtitle(null); + } + } else { + ab.setDisplayHomeAsUpEnabled(false); + ab.setHomeButtonEnabled(false); + ab.setTitle(R.string.app_name); + ab.setSubtitle(null); + } + } + } + + private void openConversation() { + this.updateActionBarTitle(); + this.invalidateOptionsMenu(); + if (xmppConnectionServiceBound) { + final Conversation conversation = getSelectedConversation(); + xmppConnectionService.getNotificationService().setOpenConversation(conversation); + sendReadMarkerIfNecessary(conversation); + } + listAdapter.notifyDataSetChanged(); + } + + public void sendReadMarkerIfNecessary(final Conversation conversation) { + if (!mActivityPaused && conversation != null) { + if (!conversation.isRead()) { + xmppConnectionService.sendReadMarker(conversation); + } else { + xmppConnectionService.markRead(conversation); + } + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.conversations, menu); + final MenuItem menuSecure = menu.findItem(R.id.action_security); + final MenuItem menuArchive = menu.findItem(R.id.action_archive); + final MenuItem menuMucDetails = menu.findItem(R.id.action_muc_details); + final MenuItem menuContactDetails = menu.findItem(R.id.action_contact_details); + final MenuItem menuAttach = menu.findItem(R.id.action_attach_file); + final MenuItem menuClearHistory = menu.findItem(R.id.action_clear_history); + final MenuItem menuAdd = menu.findItem(R.id.action_add); + final MenuItem menuInviteContact = menu.findItem(R.id.action_invite); + final MenuItem menuMute = menu.findItem(R.id.action_mute); + final MenuItem menuUnmute = menu.findItem(R.id.action_unmute); + + if (isConversationsOverviewVisable() && isConversationsOverviewHideable()) { + menuArchive.setVisible(false); + menuMucDetails.setVisible(false); + menuContactDetails.setVisible(false); + menuSecure.setVisible(false); + menuInviteContact.setVisible(false); + menuAttach.setVisible(false); + menuClearHistory.setVisible(false); + menuMute.setVisible(false); + menuUnmute.setVisible(false); + } else { + menuAdd.setVisible(!isConversationsOverviewHideable()); + if (this.getSelectedConversation() != null) { + menuSecure.setVisible(!Config.HIDE_ENCRYPTION_IN_UI); + if (this.getSelectedConversation().getNextEncryption() != Message.ENCRYPTION_NONE) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + menuSecure.setIcon(R.drawable.ic_lock_white_24dp); + } else { + menuSecure.setIcon(R.drawable.ic_action_secure); + } + } + if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) { + menuContactDetails.setVisible(false); + menuAttach.setVisible(getSelectedConversation().getAccount().httpUploadAvailable() && getSelectedConversation().getMucOptions().participating()); + menuInviteContact.setVisible(getSelectedConversation().getMucOptions().canInvite()); + menuSecure.setVisible(!Config.HIDE_ENCRYPTION_IN_UI); + menuSecure.setVisible(!Config.HIDE_PGP_IN_UI); //if pgp is hidden conferences have no choice of encryption + } else { + menuMucDetails.setVisible(false); + } + if (this.getSelectedConversation().isMuted()) { + menuMute.setVisible(false); + } else { + menuUnmute.setVisible(false); + } + } + } + return true; + } + + protected void selectPresenceToAttachFile(final int attachmentChoice, final int encryption) { + final Conversation conversation = getSelectedConversation(); + final Account account = conversation.getAccount(); + final OnPresenceSelected callback = new OnPresenceSelected() { + + @Override + public void onPresenceSelected() { + Intent intent = new Intent(); + boolean chooser = false; + String fallbackPackageId = null; + switch (attachmentChoice) { + case ATTACHMENT_CHOICE_CHOOSE_IMAGE: + intent.setAction(Intent.ACTION_GET_CONTENT); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); + } + intent.setType("image/*"); + chooser = true; + break; + case ATTACHMENT_CHOICE_TAKE_PHOTO: + Uri uri = xmppConnectionService.getFileBackend().getTakePhotoUri(); + intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE); + intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); + mPendingImageUris.clear(); + mPendingImageUris.add(uri); + break; + case ATTACHMENT_CHOICE_CHOOSE_FILE: + chooser = true; + intent.setType("*/*"); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setAction(Intent.ACTION_GET_CONTENT); + break; + case ATTACHMENT_CHOICE_RECORD_VOICE: + intent.setAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION); + fallbackPackageId = "eu.siacs.conversations.voicerecorder"; + break; + case ATTACHMENT_CHOICE_LOCATION: + intent.setAction("eu.siacs.conversations.location.request"); + fallbackPackageId = "eu.siacs.conversations.sharelocation"; + break; + } + if (intent.resolveActivity(getPackageManager()) != null) { + if (chooser) { + startActivityForResult( + Intent.createChooser(intent, getString(R.string.perform_action_with)), + attachmentChoice); + } else { + startActivityForResult(intent, attachmentChoice); + } + } else if (fallbackPackageId != null) { + startActivity(getInstallApkIntent(fallbackPackageId)); + } + } + }; + if ((account.httpUploadAvailable() || attachmentChoice == ATTACHMENT_CHOICE_LOCATION) && encryption != Message.ENCRYPTION_OTR) { + conversation.setNextCounterpart(null); + callback.onPresenceSelected(); + } else { + selectPresence(conversation, callback); + } + } + + private Intent getInstallApkIntent(final String packageId) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse("market://details?id=" + packageId)); + if (intent.resolveActivity(getPackageManager()) != null) { + return intent; + } else { + intent.setData(Uri.parse("http://play.google.com/store/apps/details?id=" + packageId)); + return intent; + } + } + + public void attachFile(final int attachmentChoice) { + switch (attachmentChoice) { + case ATTACHMENT_CHOICE_LOCATION: + getPreferences().edit().putString("recently_used_quick_action", "location").apply(); + break; + case ATTACHMENT_CHOICE_RECORD_VOICE: + getPreferences().edit().putString("recently_used_quick_action", "voice").apply(); + break; + case ATTACHMENT_CHOICE_TAKE_PHOTO: + getPreferences().edit().putString("recently_used_quick_action", "photo").apply(); + break; + case ATTACHMENT_CHOICE_CHOOSE_IMAGE: + getPreferences().edit().putString("recently_used_quick_action", "picture").apply(); + break; + } + final Conversation conversation = getSelectedConversation(); + final int encryption = conversation.getNextEncryption(); + if (encryption == Message.ENCRYPTION_PGP) { + if (hasPgp()) { + if (conversation.getContact().getPgpKeyId() != 0) { + xmppConnectionService.getPgpEngine().hasKey( + conversation.getContact(), + new UiCallback<Contact>() { + + @Override + public void userInputRequried(PendingIntent pi, + Contact contact) { + ConversationActivity.this.runIntent(pi, attachmentChoice); + } + + @Override + public void success(Contact contact) { + selectPresenceToAttachFile(attachmentChoice, encryption); + } + + @Override + public void error(int error, Contact contact) { + displayErrorDialog(error); + } + }); + } else { + final ConversationFragment fragment = (ConversationFragment) getFragmentManager() + .findFragmentByTag("conversation"); + if (fragment != null) { + fragment.showNoPGPKeyDialog(false, + new OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, + int which) { + conversation + .setNextEncryption(Message.ENCRYPTION_NONE); + xmppConnectionService.databaseBackend + .updateConversation(conversation); + selectPresenceToAttachFile(attachmentChoice, Message.ENCRYPTION_NONE); + } + }); + } + } + } else { + showInstallPgpDialog(); + } + } else { + if (encryption != Message.ENCRYPTION_AXOLOTL || !trustKeysIfNeeded(REQUEST_TRUST_KEYS_MENU, attachmentChoice)) { + selectPresenceToAttachFile(attachmentChoice, encryption); + } + } + } + + @Override + public boolean onOptionsItemSelected(final MenuItem item) { + if (item.getItemId() == android.R.id.home) { + showConversationsOverview(); + return true; + } else if (item.getItemId() == R.id.action_add) { + startActivity(new Intent(this, StartConversationActivity.class)); + return true; + } else if (getSelectedConversation() != null) { + switch (item.getItemId()) { + case R.id.action_attach_file: + attachFileDialog(); + break; + case R.id.action_archive: + this.endConversation(getSelectedConversation()); + break; + case R.id.action_contact_details: + switchToContactDetails(getSelectedConversation().getContact()); + break; + case R.id.action_muc_details: + Intent intent = new Intent(this, + ConferenceDetailsActivity.class); + intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC); + intent.putExtra("uuid", getSelectedConversation().getUuid()); + startActivity(intent); + break; + case R.id.action_invite: + inviteToConversation(getSelectedConversation()); + break; + case R.id.action_security: + selectEncryptionDialog(getSelectedConversation()); + break; + case R.id.action_clear_history: + clearHistoryDialog(getSelectedConversation()); + break; + case R.id.action_mute: + muteConversationDialog(getSelectedConversation()); + break; + case R.id.action_unmute: + unmuteConversation(getSelectedConversation()); + break; + case R.id.action_block: + BlockContactDialog.show(this, xmppConnectionService, getSelectedConversation()); + break; + case R.id.action_unblock: + BlockContactDialog.show(this, xmppConnectionService, getSelectedConversation()); + break; + default: + break; + } + return super.onOptionsItemSelected(item); + } else { + return super.onOptionsItemSelected(item); + } + } + + public void endConversation(Conversation conversation) { + endConversation(conversation, true, true); + } + + public void endConversation(Conversation conversation, boolean showOverview, boolean reinit) { + if (showOverview) { + showConversationsOverview(); + } + xmppConnectionService.archiveConversation(conversation); + if (reinit) { + if (conversationList.size() > 0) { + setSelectedConversation(conversationList.get(0)); + this.mConversationFragment.reInit(getSelectedConversation()); + } else { + setSelectedConversation(null); + if (mRedirected.compareAndSet(false, true)) { + Intent intent = new Intent(this, StartConversationActivity.class); + intent.putExtra("init", true); + startActivity(intent); + finish(); + } + } + } + } + + @SuppressLint("InflateParams") + protected void clearHistoryDialog(final Conversation conversation) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(getString(R.string.clear_conversation_history)); + View dialogView = getLayoutInflater().inflate( + R.layout.dialog_clear_history, null); + final CheckBox endConversationCheckBox = (CheckBox) dialogView + .findViewById(R.id.end_conversation_checkbox); + builder.setView(dialogView); + builder.setNegativeButton(getString(R.string.cancel), null); + builder.setPositiveButton(getString(R.string.delete_messages), + new OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + ConversationActivity.this.xmppConnectionService.clearConversationHistory(conversation); + if (endConversationCheckBox.isChecked()) { + endConversation(conversation); + } else { + updateConversationList(); + ConversationActivity.this.mConversationFragment.updateMessages(); + } + } + }); + builder.create().show(); + } + + protected void attachFileDialog() { + View menuAttachFile = findViewById(R.id.action_attach_file); + if (menuAttachFile == null) { + return; + } + PopupMenu attachFilePopup = new PopupMenu(this, menuAttachFile); + attachFilePopup.inflate(R.menu.attachment_choices); + if (new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION).resolveActivity(getPackageManager()) == null) { + attachFilePopup.getMenu().findItem(R.id.attach_record_voice).setVisible(false); + } + if (new Intent("eu.siacs.conversations.location.request").resolveActivity(getPackageManager()) == null) { + attachFilePopup.getMenu().findItem(R.id.attach_location).setVisible(false); + } + attachFilePopup.setOnMenuItemClickListener(new OnMenuItemClickListener() { + + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.attach_choose_picture: + attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE); + break; + case R.id.attach_take_picture: + attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO); + break; + case R.id.attach_choose_file: + attachFile(ATTACHMENT_CHOICE_CHOOSE_FILE); + break; + case R.id.attach_record_voice: + attachFile(ATTACHMENT_CHOICE_RECORD_VOICE); + break; + case R.id.attach_location: + attachFile(ATTACHMENT_CHOICE_LOCATION); + break; + } + return false; + } + }); + attachFilePopup.show(); + } + + public void verifyOtrSessionDialog(final Conversation conversation, View view) { + if (!conversation.hasValidOtrSession() || conversation.getOtrSession().getSessionStatus() != SessionStatus.ENCRYPTED) { + Toast.makeText(this, R.string.otr_session_not_started, Toast.LENGTH_LONG).show(); + return; + } + if (view == null) { + return; + } + PopupMenu popup = new PopupMenu(this, view); + popup.inflate(R.menu.verification_choices); + popup.setOnMenuItemClickListener(new OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem menuItem) { + Intent intent = new Intent(ConversationActivity.this, VerifyOTRActivity.class); + intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT); + intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString()); + intent.putExtra("account", conversation.getAccount().getJid().toBareJid().toString()); + switch (menuItem.getItemId()) { + case R.id.scan_fingerprint: + intent.putExtra("mode", VerifyOTRActivity.MODE_SCAN_FINGERPRINT); + break; + case R.id.ask_question: + intent.putExtra("mode", VerifyOTRActivity.MODE_ASK_QUESTION); + break; + case R.id.manual_verification: + intent.putExtra("mode", VerifyOTRActivity.MODE_MANUAL_VERIFICATION); + break; + } + startActivity(intent); + return true; + } + }); + popup.show(); + } + + protected void selectEncryptionDialog(final Conversation conversation) { + View menuItemView = findViewById(R.id.action_security); + if (menuItemView == null) { + return; + } + PopupMenu popup = new PopupMenu(this, menuItemView); + final ConversationFragment fragment = (ConversationFragment) getFragmentManager() + .findFragmentByTag("conversation"); + if (fragment != null) { + popup.setOnMenuItemClickListener(new OnMenuItemClickListener() { + + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.encryption_choice_none: + conversation.setNextEncryption(Message.ENCRYPTION_NONE); + item.setChecked(true); + break; + case R.id.encryption_choice_otr: + conversation.setNextEncryption(Message.ENCRYPTION_OTR); + item.setChecked(true); + break; + case R.id.encryption_choice_pgp: + if (hasPgp()) { + if (conversation.getAccount().getKeys().has("pgp_signature")) { + conversation.setNextEncryption(Message.ENCRYPTION_PGP); + item.setChecked(true); + } else { + announcePgp(conversation.getAccount(), conversation); + } + } else { + showInstallPgpDialog(); + } + break; + case R.id.encryption_choice_axolotl: + Log.d(Config.LOGTAG, AxolotlService.getLogprefix(conversation.getAccount()) + + "Enabled axolotl for Contact " + conversation.getContact().getJid()); + conversation.setNextEncryption(Message.ENCRYPTION_AXOLOTL); + item.setChecked(true); + break; + default: + conversation.setNextEncryption(Message.ENCRYPTION_NONE); + break; + } + xmppConnectionService.databaseBackend.updateConversation(conversation); + fragment.updateChatMsgHint(); + invalidateOptionsMenu(); + refreshUi(); + return true; + } + }); + popup.inflate(R.menu.encryption_choices); + MenuItem otr = popup.getMenu().findItem(R.id.encryption_choice_otr); + MenuItem none = popup.getMenu().findItem(R.id.encryption_choice_none); + MenuItem pgp = popup.getMenu().findItem(R.id.encryption_choice_pgp); + MenuItem axolotl = popup.getMenu().findItem(R.id.encryption_choice_axolotl); + pgp.setVisible(!Config.HIDE_PGP_IN_UI); + if (conversation.getMode() == Conversation.MODE_MULTI) { + otr.setVisible(false); + axolotl.setVisible(false); + } else if (!conversation.getAccount().getAxolotlService().isContactAxolotlCapable(conversation.getContact())) { + axolotl.setEnabled(false); + } + switch (conversation.getNextEncryption()) { + case Message.ENCRYPTION_NONE: + none.setChecked(true); + break; + case Message.ENCRYPTION_OTR: + otr.setChecked(true); + break; + case Message.ENCRYPTION_PGP: + pgp.setChecked(true); + break; + case Message.ENCRYPTION_AXOLOTL: + axolotl.setChecked(true); + break; + default: + none.setChecked(true); + break; + } + popup.show(); + } + } + + protected void muteConversationDialog(final Conversation conversation) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.disable_notifications); + final int[] durations = getResources().getIntArray(R.array.mute_options_durations); + builder.setItems(R.array.mute_options_descriptions, + new OnClickListener() { + + @Override + public void onClick(final DialogInterface dialog, final int which) { + final long till; + if (durations[which] == -1) { + till = Long.MAX_VALUE; + } else { + till = System.currentTimeMillis() + (durations[which] * 1000); + } + conversation.setMutedTill(till); + ConversationActivity.this.xmppConnectionService.databaseBackend + .updateConversation(conversation); + updateConversationList(); + ConversationActivity.this.mConversationFragment.updateMessages(); + invalidateOptionsMenu(); + } + }); + builder.create().show(); + } + + public void unmuteConversation(final Conversation conversation) { + conversation.setMutedTill(0); + this.xmppConnectionService.databaseBackend.updateConversation(conversation); + updateConversationList(); + ConversationActivity.this.mConversationFragment.updateMessages(); + invalidateOptionsMenu(); + } + + @Override + public void onBackPressed() { + if (!isConversationsOverviewVisable()) { + showConversationsOverview(); + } else { + moveTaskToBack(true); + } + } + + @Override + public boolean onKeyUp(int key, KeyEvent event) { + int rotation = getWindowManager().getDefaultDisplay().getRotation(); + final int upKey; + final int downKey; + switch (rotation) { + case Surface.ROTATION_90: + upKey = KeyEvent.KEYCODE_DPAD_LEFT; + downKey = KeyEvent.KEYCODE_DPAD_RIGHT; + break; + case Surface.ROTATION_180: + upKey = KeyEvent.KEYCODE_DPAD_DOWN; + downKey = KeyEvent.KEYCODE_DPAD_UP; + break; + case Surface.ROTATION_270: + upKey = KeyEvent.KEYCODE_DPAD_RIGHT; + downKey = KeyEvent.KEYCODE_DPAD_LEFT; + break; + default: + upKey = KeyEvent.KEYCODE_DPAD_UP; + downKey = KeyEvent.KEYCODE_DPAD_DOWN; + } + final boolean modifier = event.isCtrlPressed() || event.isAltPressed(); + if (modifier && key == KeyEvent.KEYCODE_TAB && isConversationsOverviewHideable()) { + toggleConversationsOverview(); + return true; + } else if (modifier && key == downKey) { + if (isConversationsOverviewHideable() && !isConversationsOverviewVisable()) { + showConversationsOverview(); + ; + } + return selectDownConversation(); + } else if (modifier && key == upKey) { + if (isConversationsOverviewHideable() && !isConversationsOverviewVisable()) { + showConversationsOverview(); + } + return selectUpConversation(); + } else if (modifier && key == KeyEvent.KEYCODE_1) { + return openConversationByIndex(0); + } else if (modifier && key == KeyEvent.KEYCODE_2) { + return openConversationByIndex(1); + } else if (modifier && key == KeyEvent.KEYCODE_3) { + return openConversationByIndex(2); + } else if (modifier && key == KeyEvent.KEYCODE_4) { + return openConversationByIndex(3); + } else if (modifier && key == KeyEvent.KEYCODE_5) { + return openConversationByIndex(4); + } else if (modifier && key == KeyEvent.KEYCODE_6) { + return openConversationByIndex(5); + } else if (modifier && key == KeyEvent.KEYCODE_7) { + return openConversationByIndex(6); + } else if (modifier && key == KeyEvent.KEYCODE_8) { + return openConversationByIndex(7); + } else if (modifier && key == KeyEvent.KEYCODE_9) { + return openConversationByIndex(8); + } else if (modifier && key == KeyEvent.KEYCODE_0) { + return openConversationByIndex(9); + } else { + return super.onKeyUp(key, event); + } + } + + private void toggleConversationsOverview() { + if (isConversationsOverviewVisable()) { + hideConversationsOverview(); + if (mConversationFragment != null) { + mConversationFragment.setFocusOnInputField(); + } + } else { + showConversationsOverview(); + } + } + + private boolean selectUpConversation() { + if (this.mSelectedConversation != null) { + int index = this.conversationList.indexOf(this.mSelectedConversation); + if (index > 0) { + return openConversationByIndex(index - 1); + } + } + return false; + } + + private boolean selectDownConversation() { + if (this.mSelectedConversation != null) { + int index = this.conversationList.indexOf(this.mSelectedConversation); + if (index != -1 && index < this.conversationList.size() - 1) { + return openConversationByIndex(index + 1); + } + } + return false; + } + + private boolean openConversationByIndex(int index) { + try { + this.conversationWasSelectedByKeyboard = true; + setSelectedConversation(this.conversationList.get(index)); + this.mConversationFragment.reInit(getSelectedConversation()); + if (index > listView.getLastVisiblePosition() - 1 || index < listView.getFirstVisiblePosition() + 1) { + this.listView.setSelection(index); + } + openConversation(); + return true; + } catch (IndexOutOfBoundsException e) { + return false; + } + } + + @Override + protected void onNewIntent(final Intent intent) { + if (xmppConnectionServiceBound) { + if (intent != null && VIEW_CONVERSATION.equals(intent.getType())) { + handleViewConversationIntent(intent); + } + } else { + setIntent(intent); + } + } + + @Override + public void onStart() { + super.onStart(); + this.mRedirected.set(false); + if (this.xmppConnectionServiceBound) { + this.onBackendConnected(); + } + if (conversationList.size() >= 1) { + this.onConversationUpdate(); + } + } + + @Override + public void onPause() { + listView.discardUndo(); + super.onPause(); + this.mActivityPaused = true; + if (this.xmppConnectionServiceBound) { + this.xmppConnectionService.getNotificationService().setIsInForeground(false); + } + } + + @Override + public void onResume() { + super.onResume(); + final int theme = findTheme(); + final boolean usingEnterKey = usingEnterKey(); + if (this.mTheme != theme || usingEnterKey != mUsingEnterKey) { + recreate(); + } + this.mActivityPaused = false; + if (this.xmppConnectionServiceBound) { + this.xmppConnectionService.getNotificationService().setIsInForeground(true); + } + + if (!isConversationsOverviewVisable() || !isConversationsOverviewHideable()) { + sendReadMarkerIfNecessary(getSelectedConversation()); + } + + AppUpdate(); + } + + @Override + public void onSaveInstanceState(final Bundle savedInstanceState) { + Conversation conversation = getSelectedConversation(); + if (conversation != null) { + savedInstanceState.putString(STATE_OPEN_CONVERSATION, + conversation.getUuid()); + } + savedInstanceState.putBoolean(STATE_PANEL_OPEN, + isConversationsOverviewVisable()); + if (this.mPendingImageUris.size() >= 1) { + savedInstanceState.putString(STATE_PENDING_URI, this.mPendingImageUris.get(0).toString()); + } + super.onSaveInstanceState(savedInstanceState); + } + + @Override + void onBackendConnected() { + this.xmppConnectionService.getNotificationService().setIsInForeground(true); + updateConversationList(); + + if (mPendingConferenceInvite != null) { + mPendingConferenceInvite.execute(this); + mPendingConferenceInvite = null; + } + + if (xmppConnectionService.getAccounts().size() == 0) { + if (mRedirected.compareAndSet(false, true)) { + if (Config.X509_VERIFICATION) { + startActivity(new Intent(this, ManageAccountActivity.class)); + } else { + startActivity(new Intent(this, EditAccountActivity.class)); + } + finish(); + } + } else if (conversationList.size() <= 0) { + if (mRedirected.compareAndSet(false, true)) { + Intent intent = new Intent(this, StartConversationActivity.class); + intent.putExtra("init", true); + startActivity(intent); + finish(); + } + } else if (getIntent() != null && VIEW_CONVERSATION.equals(getIntent().getType())) { + handleViewConversationIntent(getIntent()); + } else if (selectConversationByUuid(mOpenConverstaion)) { + if (mPanelOpen) { + showConversationsOverview(); + } else { + if (isConversationsOverviewHideable()) { + openConversation(); + } + } + this.mConversationFragment.reInit(getSelectedConversation()); + mOpenConverstaion = null; + } else if (getSelectedConversation() == null) { + showConversationsOverview(); + mPendingImageUris.clear(); + mPendingFileUris.clear(); + mPendingGeoUri = null; + setSelectedConversation(conversationList.get(0)); + this.mConversationFragment.reInit(getSelectedConversation()); + } else { + this.mConversationFragment.messageListAdapter.updatePreferences(); + this.mConversationFragment.messagesView.invalidateViews(); + this.mConversationFragment.setupIme(); + } + + if (!forbidProcessingPendings) { + for (Iterator<Uri> i = mPendingImageUris.iterator(); i.hasNext(); i.remove()) { + Uri foo = i.next(); + attachImageToConversation(getSelectedConversation(), foo); + } + + for (Iterator<Uri> i = mPendingFileUris.iterator(); i.hasNext(); i.remove()) { + attachFileToConversation(getSelectedConversation(), i.next()); + } + + if (mPendingGeoUri != null) { + attachLocationToConversation(getSelectedConversation(), mPendingGeoUri); + mPendingGeoUri = null; + } + } + forbidProcessingPendings = false; + + ExceptionHelper.checkForCrash(this, this.xmppConnectionService); + setIntent(new Intent()); + } + + private void handleViewConversationIntent(final Intent intent) { + final String uuid = intent.getStringExtra(CONVERSATION); + final String downloadUuid = intent.getStringExtra(MESSAGE); + final String text = intent.getStringExtra(TEXT); + final String nick = intent.getStringExtra(NICK); + final boolean pm = intent.getBooleanExtra(PRIVATE_MESSAGE, false); + if (selectConversationByUuid(uuid)) { + this.mConversationFragment.reInit(getSelectedConversation()); + if (nick != null) { + if (pm) { + Jid jid = getSelectedConversation().getJid(); + try { + Jid next = Jid.fromParts(jid.getLocalpart(), jid.getDomainpart(), nick); + this.mConversationFragment.privateMessageWith(next); + } catch (final InvalidJidException ignored) { + //do nothing + } + } else { + this.mConversationFragment.highlightInConference(nick); + } + } else { + this.mConversationFragment.appendText(text); + } + hideConversationsOverview(); + openConversation(); + if (mContentView instanceof SlidingPaneLayout) { + updateActionBarTitle(true); //fixes bug where slp isn't properly closed yet + } + if (downloadUuid != null) { + final Message message = mSelectedConversation.findMessageWithFileAndUuid(downloadUuid); + if (message != null) { + mConversationFragment.messageListAdapter.startDownloadable(message); + } + } + } + } + + private boolean selectConversationByUuid(String uuid) { + if (uuid == null) { + return false; + } + for (Conversation aConversationList : conversationList) { + if (aConversationList.getUuid().equals(uuid)) { + setSelectedConversation(aConversationList); + return true; + } + } + return false; + } + + @Override + protected void unregisterListeners() { + super.unregisterListeners(); + xmppConnectionService.getNotificationService().setOpenConversation(null); + } + + @SuppressLint("NewApi") + private static List<Uri> extractUriFromIntent(final Intent intent) { + List<Uri> uris = new ArrayList<>(); + Uri uri = intent.getData(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && uri == null) { + ClipData clipData = intent.getClipData(); + for (int i = 0; i < clipData.getItemCount(); ++i) { + uris.add(clipData.getItemAt(i).getUri()); + } + } else { + uris.add(uri); + } + return uris; + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, + final Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (resultCode == RESULT_OK) { + if (requestCode == REQUEST_DECRYPT_PGP) { + mConversationFragment.onActivityResult(requestCode, resultCode, data); + } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) { + mPendingImageUris.clear(); + mPendingImageUris.addAll(extractUriFromIntent(data)); + if (xmppConnectionServiceBound) { + for (Iterator<Uri> i = mPendingImageUris.iterator(); i.hasNext(); i.remove()) { + attachImageToConversation(getSelectedConversation(), i.next()); + } + } + } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_FILE || requestCode == ATTACHMENT_CHOICE_RECORD_VOICE) { + mPendingFileUris.clear(); + mPendingFileUris.addAll(extractUriFromIntent(data)); + if (xmppConnectionServiceBound) { + for (Iterator<Uri> i = mPendingFileUris.iterator(); i.hasNext(); i.remove()) { + attachFileToConversation(getSelectedConversation(), i.next()); + } + } + } else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) { + if (mPendingImageUris.size() == 1) { + Uri uri = mPendingImageUris.get(0); + if (xmppConnectionServiceBound) { + attachImageToConversation(getSelectedConversation(), uri); + mPendingImageUris.clear(); + } + Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); + intent.setData(uri); + sendBroadcast(intent); + } else { + mPendingImageUris.clear(); + } + } else if (requestCode == ATTACHMENT_CHOICE_LOCATION) { + double latitude = data.getDoubleExtra("latitude", 0); + double longitude = data.getDoubleExtra("longitude", 0); + this.mPendingGeoUri = Uri.parse("geo:" + String.valueOf(latitude) + "," + String.valueOf(longitude)); + if (xmppConnectionServiceBound) { + attachLocationToConversation(getSelectedConversation(), mPendingGeoUri); + this.mPendingGeoUri = null; + } + } else if (requestCode == REQUEST_TRUST_KEYS_TEXT || requestCode == REQUEST_TRUST_KEYS_MENU) { + this.forbidProcessingPendings = !xmppConnectionServiceBound; + mConversationFragment.onActivityResult(requestCode, resultCode, data); + } + } else { + mPendingImageUris.clear(); + mPendingFileUris.clear(); + if (requestCode == ConversationActivity.REQUEST_DECRYPT_PGP) { + mConversationFragment.onActivityResult(requestCode, resultCode, data); + } + } + } + + private void attachLocationToConversation(Conversation conversation, Uri uri) { + if (conversation == null) { + return; + } + xmppConnectionService.attachLocationToConversation(conversation, uri, new UiCallback<Message>() { + + @Override + public void success(Message message) { + xmppConnectionService.sendMessage(message); + } + + @Override + public void error(int errorCode, Message object) { + + } + + @Override + public void userInputRequried(PendingIntent pi, Message object) { + + } + }); + } + + private void attachFileToConversation(Conversation conversation, Uri uri) { + if (conversation == null) { + return; + } + prepareFileToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_file), Toast.LENGTH_LONG); + prepareFileToast.show(); + xmppConnectionService.attachFileToConversation(conversation, uri, new UiCallback<Message>() { + @Override + public void success(Message message) { + hidePrepareFileToast(); + xmppConnectionService.sendMessage(message); + } + + @Override + public void error(int errorCode, Message message) { + displayErrorDialog(errorCode); + } + + @Override + public void userInputRequried(PendingIntent pi, Message message) { + + } + }); + } + + private void attachImageToConversation(Conversation conversation, Uri uri) { + if (conversation == null) { + return; + } + prepareFileToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_image), Toast.LENGTH_LONG); + prepareFileToast.show(); + xmppConnectionService.attachImageToConversation(conversation, uri, + new UiCallback<Message>() { + + @Override + public void userInputRequried(PendingIntent pi, + Message object) { + hidePrepareFileToast(); + } + + @Override + public void success(Message message) { + xmppConnectionService.sendMessage(message); + } + + @Override + public void error(int error, Message message) { + hidePrepareFileToast(); + displayErrorDialog(error); + } + }); + } + + private void hidePrepareFileToast() { + if (prepareFileToast != null) { + runOnUiThread(new Runnable() { + + @Override + public void run() { + prepareFileToast.cancel(); + } + }); + } + } + + public void updateConversationList() { + xmppConnectionService + .populateWithOrderedConversations(conversationList); + if (swipedConversation != null) { + if (swipedConversation.isRead()) { + conversationList.remove(swipedConversation); + } else { + listView.discardUndo(); + } + } + listAdapter.notifyDataSetChanged(); + } + + public void runIntent(PendingIntent pi, int requestCode) { + try { + this.startIntentSenderForResult(pi.getIntentSender(), requestCode, + null, 0, 0, 0); + } catch (final SendIntentException ignored) { + } + } + + public void encryptTextMessage(Message message) { + xmppConnectionService.getPgpEngine().encrypt(message, + new UiCallback<Message>() { + + @Override + public void userInputRequried(PendingIntent pi, + Message message) { + ConversationActivity.this.runIntent(pi, + ConversationActivity.REQUEST_SEND_MESSAGE); + } + + @Override + public void success(Message message) { + message.setEncryption(Message.ENCRYPTION_DECRYPTED); + xmppConnectionService.sendMessage(message); + } + + @Override + public void error(int error, Message message) { + + } + }); + } + + public boolean useSendButtonToIndicateStatus() { + return getPreferences().getBoolean("send_button_status", false); + } + + public boolean indicateReceived() { + return getPreferences().getBoolean("indicate_received", false); + } + + public boolean useWhiteBackground() { + return getPreferences().getBoolean("use_white_background", false); + } + + protected boolean trustKeysIfNeeded(int requestCode) { + return trustKeysIfNeeded(requestCode, ATTACHMENT_CHOICE_INVALID); + } + + protected boolean trustKeysIfNeeded(int requestCode, int attachmentChoice) { + AxolotlService axolotlService = mSelectedConversation.getAccount().getAxolotlService(); + boolean hasPendingKeys = !axolotlService.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED, + mSelectedConversation.getContact()).isEmpty() + || !axolotlService.findDevicesWithoutSession(mSelectedConversation).isEmpty(); + boolean hasNoTrustedKeys = axolotlService.getNumTrustedKeys(mSelectedConversation.getContact()) == 0; + if (hasPendingKeys || hasNoTrustedKeys) { + axolotlService.createSessionsIfNeeded(mSelectedConversation); + Intent intent = new Intent(getApplicationContext(), TrustKeysActivity.class); + intent.putExtra("contact", mSelectedConversation.getContact().getJid().toBareJid().toString()); + intent.putExtra("account", mSelectedConversation.getAccount().getJid().toBareJid().toString()); + intent.putExtra("choice", attachmentChoice); + intent.putExtra("has_no_trusted", hasNoTrustedKeys); + startActivityForResult(intent, requestCode); + return true; + } else { + return false; + } + } + + @Override + protected void refreshUiReal() { + updateConversationList(); + if (conversationList.size() > 0) { + ConversationActivity.this.mConversationFragment.updateMessages(); + updateActionBarTitle(); + invalidateOptionsMenu(); + } + } + + @Override + public void onAccountUpdate() { + this.refreshUi(); + } + + @Override + public void onConversationUpdate() { + this.refreshUi(); + } + + @Override + public void onRosterUpdate() { + this.refreshUi(); + } + + @Override + public void OnUpdateBlocklist(Status status) { + this.refreshUi(); + } + + public void unblockConversation(final Blockable conversation) { + xmppConnectionService.sendUnblockRequest(conversation); + } + + public boolean enterIsSend() { + return getPreferences().getBoolean("enter_is_send", false); + } + + @Override + public void onShowErrorToast(final int resId) { + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(ConversationActivity.this, resId, Toast.LENGTH_SHORT).show(); + } + }); + } + + public boolean highlightSelectedConversations() { + return !isConversationsOverviewHideable() || this.conversationWasSelectedByKeyboard; + } } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 7b83c2bef..fd3426ca7 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -1011,18 +1011,18 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa if (conversation.getMode() == Conversation.MODE_SINGLE) { ChatState state = conversation.getIncomingChatState(); if (state == ChatState.COMPOSING) { - this.messageList.add(Message.createStatusMessage(conversation, getString(R.string.contact_is_typing, conversation.getName()))); + //this.messageList.add(Message.createStatusMessage(conversation, getString(R.string.contact_is_typing, conversation.getName()))); } else if (state == ChatState.PAUSED) { - this.messageList.add(Message.createStatusMessage(conversation, getString(R.string.contact_has_stopped_typing, conversation.getName()))); + //this.messageList.add(Message.createStatusMessage(conversation, getString(R.string.contact_has_stopped_typing, conversation.getName()))); } else { for (int i = this.messageList.size() - 1; i >= 0; --i) { if (this.messageList.get(i).getStatus() == Message.STATUS_RECEIVED) { return; } else { if (this.messageList.get(i).getStatus() == Message.STATUS_SEND_DISPLAYED) { - this.messageList.add(i + 1, - Message.createStatusMessage(conversation, getString(R.string.contact_has_read_up_to_this_point, conversation.getName()))); - return; +// this.messageList.add(i + 1, +// Message.createStatusMessage(conversation, getString(R.string.contact_has_read_up_to_this_point, conversation.getName()))); +// return; } } } diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java index 180fc8b84..9ec527d47 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java @@ -29,9 +29,9 @@ import android.widget.RelativeLayout; import android.widget.TableLayout; import android.widget.TextView; import android.widget.Toast; - +import eu.siacs.conversations.Config; +import org.whispersystems.libaxolotl.IdentityKey; import java.util.Set; - import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.axolotl.AxolotlService; @@ -365,7 +365,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate this.mAxolotlFingerprint = (TextView) findViewById(R.id.axolotl_fingerprint); this.mAxolotlFingerprintBox = (RelativeLayout) findViewById(R.id.axolotl_fingerprint_box); this.mAxolotlFingerprintToClipboardButton = (ImageButton) findViewById(R.id.action_copy_axolotl_to_clipboard); - this.mRegenerateAxolotlKeyButton = (ImageButton) findViewById(R.id.action_regenerate_axolotl_key); + this.mRegenerateAxolotlKeyButton = (ImageButton) findViewById(R.id.action_regenerate_omemo_key); this.keysCard = (LinearLayout) findViewById(R.id.other_device_keys_card); this.keys = (LinearLayout) findViewById(R.id.other_device_keys); this.mSaveButton = (Button) findViewById(R.id.save_button); @@ -538,7 +538,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate } if (!mInitMode) { this.mAvatar.setVisibility(View.VISIBLE); - this.mAvatar.setImageBitmap(avatarService().get(this.mAccount, getPixel(72))); + this.mAvatar.setImageBitmap(avatarService().get(this.mAccount, getPixel(Config.AVATAR_SIZE))); } if (this.mAccount.isOptionSet(Account.OPTION_REGISTER)) { this.mRegisterNew.setVisibility(View.VISIBLE); diff --git a/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java b/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java index e01490f90..9e64a6805 100644 --- a/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java @@ -172,7 +172,7 @@ public class PublishProfilePictureActivity extends XmppActivity { if (this.account.getAvatar() != null || this.defaultUri == null) { this.avatar.setImageBitmap(avatarService().get(account, - getPixel(194))); + getPixel(Config.AVATAR_SIZE))); if (this.defaultUri != null) { this.avatar .setOnLongClickListener(this.backToDefaultListener); @@ -219,7 +219,7 @@ public class PublishProfilePictureActivity extends XmppActivity { protected void loadImageIntoPreview(Uri uri) { Bitmap bm = xmppConnectionService.getFileBackend().cropCenterSquare( - uri, 384); + uri, Config.AVATAR_SIZE); if (bm == null) { disablePublishButton(); this.hintOrWarning.setTextColor(getWarningTextColor()); diff --git a/src/main/java/eu/siacs/conversations/ui/UpdaterActivity.java b/src/main/java/eu/siacs/conversations/ui/UpdaterActivity.java new file mode 100644 index 000000000..4dccdfe5b --- /dev/null +++ b/src/main/java/eu/siacs/conversations/ui/UpdaterActivity.java @@ -0,0 +1,262 @@ +package eu.siacs.conversations.ui; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.DownloadManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.Uri; +import android.os.Bundle; +import android.os.Environment; +import android.util.Log; +import android.view.WindowManager; +import android.widget.TextView; +import android.widget.Toast; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; + +import eu.siacs.conversations.Config; +import eu.siacs.conversations.R; +import eu.siacs.conversations.services.UpdaterWebService; + +public class UpdaterActivity extends Activity { + + private UpdateReceiver receiver = null; + private int versionCode = 0; + String appURI = ""; + + + /* + // run AppUpdater + Log.d(Config.LOGTAG, "Start automatic AppUpdater"); + Intent AppUpdater = new Intent(this, UpdaterActivity.class); + startActivity(AppUpdater); + */ + + private DownloadManager downloadManager; + private long downloadReference; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + //set activity + setContentView(R.layout.activity_updater); + TextView textView = (TextView) findViewById(R.id.updater); + textView.setText(R.string.update_info); + + //Broadcast receiver for our Web Request + IntentFilter filter = new IntentFilter(UpdateReceiver.PROCESS_RESPONSE); + filter.addCategory(Intent.CATEGORY_DEFAULT); + receiver = new UpdateReceiver(); + registerReceiver(receiver, filter); + + //Broadcast receiver for the download manager + filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE); + registerReceiver(downloadReceiver, filter); + + //check of internet is available before making a web service request + if (isNetworkAvailable(this)) { + Intent msgIntent = new Intent(this, UpdaterWebService.class); + msgIntent.putExtra(UpdaterWebService.REQUEST_STRING, Config.UPDATE_URL); + + Toast.makeText(getApplicationContext(), + getText(R.string.checking_for_updates), + Toast.LENGTH_SHORT).show(); + startService(msgIntent); + } + } + + @Override + public void onDestroy() { + //unregister your receivers + this.unregisterReceiver(receiver); + this.unregisterReceiver(downloadReceiver); + super.onDestroy(); + //enable touch events + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); + } + + @Override + public void onSaveInstanceState(Bundle savedInstanceState) { + super.onSaveInstanceState(savedInstanceState); + } + + @Override + public void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + } + + //check for internet connection + private boolean isNetworkAvailable(Context context) { + ConnectivityManager connectivity = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + if (connectivity != null) { + NetworkInfo[] info = connectivity.getAllNetworkInfo(); + if (info != null) { + for (int i = 0; i < info.length; i++) { + Log.d(Config.LOGTAG, "AppUpdater: " + String.valueOf(i)); + if (info[i].getState() == NetworkInfo.State.CONNECTED) { + Log.d(Config.LOGTAG, "AppUpdater: connected to update Server!"); + return true; + } + } + } + } + return false; + } + + //broadcast receiver to get notification when the web request finishes + public class UpdateReceiver extends BroadcastReceiver { + + public static final String PROCESS_RESPONSE = "eu.siacs.conversations.intent.action.PROCESS_RESPONSE"; + + @Override + public void onReceive(Context context, Intent intent) { + //disable touch events + getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, + WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); + + String reponseMessage = intent.getStringExtra(UpdaterWebService.RESPONSE_MESSAGE); + Log.d(Config.LOGTAG, "AppUpdater: Reponse: " + reponseMessage); + + if (reponseMessage == "" || reponseMessage.isEmpty() || reponseMessage == null) { + Toast.makeText(getApplicationContext(), + getText(R.string.failed), + Toast.LENGTH_LONG).show(); + Log.e(Config.LOGTAG, "AppUpdater: error connecting to server"); + UpdaterActivity.this.finish(); + } else { + Log.d(Config.LOGTAG, "AppUpdater: connecting to server"); + //parse the JSON reponse + JSONObject reponseObj; + + try { + //if the reponse was successful check further + reponseObj = new JSONObject(reponseMessage); + boolean success = reponseObj.getBoolean("success"); + if (success) { + //Overall information about the contents of a package + //This correponds to all of the information collected from AndroidManifest.xml. + PackageInfo pInfo = null; + try { + pInfo = getPackageManager().getPackageInfo(getPackageName(), 0); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + //get the app version Name for display + final String versionName = pInfo.versionName; + final int versionCode = pInfo.versionCode; + //get the latest version from the JSON string + int latestVersionCode = reponseObj.getInt("latestVersionCode"); + String latestVersion = reponseObj.getString("latestVersion"); + String changelog = reponseObj.getString("changelog"); + //get the lastest application URI from the JSON string + appURI = reponseObj.getString("appURI"); + //check if we need to upgrade? + if (latestVersionCode > versionCode) { + Log.d(Config.LOGTAG, "AppUpdater: update available"); + //delete old downloaded version files + File dir = new File(getExternalFilesDir(null), Environment.DIRECTORY_DOWNLOADS); + Log.d(Config.LOGTAG, "AppUpdater: delete old update files in: " + dir); + if (dir.isDirectory()) { + String[] children = dir.list(); + for (int i = 0; i < children.length; i++) { + new File(dir, children[i]).delete(); + } + } + //enable touch events + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); + + //oh yeah we do need an upgrade, let the user know send an alert message + AlertDialog.Builder builder = new AlertDialog.Builder(UpdaterActivity.this); + builder.setCancelable(false); + + String UpdateMessageInfo = getResources().getString(R.string.update_available); + builder.setMessage(String.format(UpdateMessageInfo, latestVersion, changelog, versionName)) + .setPositiveButton(R.string.update, new DialogInterface.OnClickListener() { + //if the user agrees to upgrade + public void onClick(DialogInterface dialog, int id) { + //disable touch events + getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, + WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); + //start downloading the file using the download manager + downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); + Uri Download_Uri = Uri.parse(appURI); + DownloadManager.Request request = new DownloadManager.Request(Download_Uri); + request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI); + request.setAllowedOverRoaming(false); + request.setTitle("Conversations Update"); + request.setDestinationInExternalFilesDir(UpdaterActivity.this, Environment.DIRECTORY_DOWNLOADS, "Conversations" + versionName + ".apk"); + downloadReference = downloadManager.enqueue(request); + Toast.makeText(getApplicationContext(), + getText(R.string.download_started), + Toast.LENGTH_LONG).show(); + } + }) + .setNegativeButton(R.string.remind_later, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + // User cancelled the dialog + UpdaterActivity.this.finish(); + } + }); + //show the alert message + builder.create().show(); + } else { + Toast.makeText(getApplicationContext(), + getText(R.string.no_update_available), + Toast.LENGTH_SHORT).show(); + Log.d(Config.LOGTAG, "AppUpdater: no update available"); + UpdaterActivity.this.finish(); + } + } else { + Toast.makeText(getApplicationContext(), + getText(R.string.failed), + Toast.LENGTH_LONG).show(); + Log.e(Config.LOGTAG, "AppUpdater: contact to server not successfull"); + UpdaterActivity.this.finish(); + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + + } + + } + + //broadcast receiver to get notification about ongoing downloads + private BroadcastReceiver downloadReceiver = new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + //check if the broadcast message is for our Enqueued download + long referenceId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); + if (downloadReference == referenceId) { + + Log.d(Config.LOGTAG, "AppUpdater: Downloading of the new app version complete. Starting installation"); + //start the installation of the latest version + Intent installIntent = new Intent(Intent.ACTION_VIEW); + installIntent.setDataAndType(downloadManager.getUriForDownloadedFile(downloadReference), + "application/vnd.android.package-archive"); + installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(installIntent); + UpdaterActivity.this.finish(); + } + } + }; + + //disable back button + @Override + public void onBackPressed() { + } +} diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java index aa03a61df..9dae18157 100644 --- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java @@ -334,6 +334,9 @@ public abstract class XmppActivity extends Activity { case R.id.action_settings: startActivity(new Intent(this, SettingsActivity.class)); break; + case R.id.action_check_updates: + startActivity(new Intent(this, UpdaterActivity.class)); + break; case R.id.action_accounts: startActivity(new Intent(this, ManageAccountActivity.class)); break; diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java index 98250af94..c1b9cbdf6 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java @@ -43,7 +43,7 @@ public class AccountAdapter extends ArrayAdapter<Account> { } TextView statusView = (TextView) view.findViewById(R.id.account_status); ImageView imageView = (ImageView) view.findViewById(R.id.account_image); - imageView.setImageBitmap(activity.avatarService().get(account, activity.getPixel(48))); + imageView.setImageBitmap(activity.avatarService().get(account, activity.getPixel(56))); statusView.setText(getContext().getString(account.getStatus().getReadableId())); switch (account.getStatus()) { case ONLINE: diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java index 019b83d35..039efe3c5 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -121,6 +121,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { boolean error = false; if (viewHolder.indicatorReceived != null) { viewHolder.indicatorReceived.setVisibility(View.GONE); + viewHolder.indicatorRead.setVisibility(View.GONE); } boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI && message.getMergedStatus() <= Message.STATUS_RECEIVED; @@ -158,6 +159,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { case Message.STATUS_SEND_DISPLAYED: if (mIndicateReceived) { viewHolder.indicatorReceived.setVisibility(View.VISIBLE); + viewHolder.indicatorRead.setVisibility(View.VISIBLE); } break; case Message.STATUS_SEND_FAILED: @@ -396,7 +398,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { viewHolder.messageBody.setVisibility(View.GONE); viewHolder.image.setVisibility(View.VISIBLE); FileParams params = message.getFileParams(); - double target = metrics.density * 288; + double target = metrics.density * 200; int scalledW; int scalledH; if (params.width <= params.height) { @@ -450,6 +452,8 @@ public class MessageAdapter extends ArrayAdapter<Message> { .findViewById(R.id.message_time); viewHolder.indicatorReceived = (ImageView) view .findViewById(R.id.indicator_received); + viewHolder.indicatorRead = (ImageView) view + .findViewById(R.id.indicator_read); break; case RECEIVED: view = activity.getLayoutInflater().inflate( @@ -670,6 +674,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { protected ImageView image; protected ImageView indicator; protected ImageView indicatorReceived; + protected ImageView indicatorRead; protected TextView time; protected TextView messageBody; protected ImageView contact_picture; diff --git a/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java b/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java index 0f1828473..6d1d1074d 100644 --- a/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java @@ -94,7 +94,7 @@ public class ExceptionHelper { Conversation conversation = null; try { conversation = service.findOrCreateConversation(finalAccount, - Jid.fromString("bugs@siacs.eu"), false); + Jid.fromString("bugs@pix-art.de"), false); } catch (final InvalidJidException ignored) { } Message message = new Message(conversation, report diff --git a/src/main/java/eu/siacs/conversations/utils/GeoHelper.java b/src/main/java/eu/siacs/conversations/utils/GeoHelper.java index 74f91a98b..cd97e1105 100644 --- a/src/main/java/eu/siacs/conversations/utils/GeoHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/GeoHelper.java @@ -9,6 +9,7 @@ import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; +import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; @@ -40,6 +41,7 @@ public class GeoHelper { return intents; } final Conversation conversation = message.getConversation(); + final Contact contact = message.getContact(); String label; if (conversation.getMode() == Conversation.MODE_SINGLE && message.getStatus() == Message.STATUS_RECEIVED) { try { @@ -62,6 +64,16 @@ public class GeoHelper { else { locationPluginIntent.putExtra("jid",conversation.getAccount().getJid().toString()); } + } else { + if (message.getStatus() == Message.STATUS_RECEIVED) { + if (contact != null) { + locationPluginIntent.putExtra("name",contact.getDisplayName()); + } + locationPluginIntent.putExtra("jid",message.getCounterpart().toString()); + } + else { + locationPluginIntent.putExtra("jid",conversation.getAccount().getJid().toString()); + } } intents.add(locationPluginIntent); @@ -74,4 +86,4 @@ public class GeoHelper { intents.add(httpIntent); return intents; } -} +}
\ No newline at end of file diff --git a/src/main/java/eu/siacs/conversations/xmpp/OnNewKeysAvailable.java b/src/main/java/eu/siacs/conversations/xmpp/OnNewKeysAvailable.java new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/main/java/eu/siacs/conversations/xmpp/OnNewKeysAvailable.java diff --git a/src/main/res/drawable-hdpi/ic_read_indicator.png b/src/main/res/drawable-hdpi/ic_read_indicator.png Binary files differnew file mode 100644 index 000000000..21fbf4eb9 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_read_indicator.png diff --git a/src/main/res/drawable-mdpi/ic_read_indicator.png b/src/main/res/drawable-mdpi/ic_read_indicator.png Binary files differnew file mode 100644 index 000000000..4cabf6ec2 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_read_indicator.png diff --git a/src/main/res/drawable-xhdpi/ic_read_indicator.png b/src/main/res/drawable-xhdpi/ic_read_indicator.png Binary files differnew file mode 100644 index 000000000..216c33128 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_read_indicator.png diff --git a/src/main/res/drawable-xxhdpi/ic_read_indicator.png b/src/main/res/drawable-xxhdpi/ic_read_indicator.png Binary files differnew file mode 100644 index 000000000..66caba6ae --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_read_indicator.png diff --git a/src/main/res/drawable-xxxhdpi/ic_read_indicator.png b/src/main/res/drawable-xxxhdpi/ic_read_indicator.png Binary files differnew file mode 100644 index 000000000..aef5c7bdc --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_read_indicator.png diff --git a/src/main/res/drawable/message_border.xml b/src/main/res/drawable/message_border.xml new file mode 100644 index 000000000..990d02886 --- /dev/null +++ b/src/main/res/drawable/message_border.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle" > + + <corners android:radius="2dp" /> + + <padding + android:bottom="1.5dp" + android:left="1.5dp" + android:right="1.5dp" + android:top="1.5dp" /> + + <solid android:color="@color/black12" /> + +</shape>
\ No newline at end of file diff --git a/src/main/res/layout/account_row.xml b/src/main/res/layout/account_row.xml index cac9a9fa6..f0d0c903d 100644 --- a/src/main/res/layout/account_row.xml +++ b/src/main/res/layout/account_row.xml @@ -15,6 +15,8 @@ android:layout_alignParentLeft="true" android:src="@drawable/ic_profile" android:contentDescription="@string/account_image_description" + android:background="@drawable/message_border" + android:padding="1dp" app:riv_corner_radius="2dp" /> <LinearLayout diff --git a/src/main/res/layout/activity_contact_details.xml b/src/main/res/layout/activity_contact_details.xml index c35f26bb1..bb43e2745 100644 --- a/src/main/res/layout/activity_contact_details.xml +++ b/src/main/res/layout/activity_contact_details.xml @@ -22,18 +22,28 @@ <QuickContactBadge android:id="@+id/details_contact_badge" - android:layout_width="72dp" - android:layout_height="72dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:maxHeight="384dp" + android:maxWidth="384dp" + android:scaleType="centerCrop" android:layout_alignParentTop="true" - android:scaleType="centerCrop" /> + android:layout_centerHorizontal="true" + android:background="@drawable/message_border" + android:padding="1dp" + android:adjustViewBounds="true"/> <LinearLayout android:id="@+id/details_jidbox" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginLeft="16dp" - android:layout_toRightOf="@+id/details_contact_badge" - android:orientation="vertical" > + android:orientation="vertical" + android:layout_below="@+id/details_contact_badge" + android:layout_alignParentRight="true" + android:layout_alignParentEnd="true" + android:layout_marginTop="16dp" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true"> <TextView android:id="@+id/details_contactjid" @@ -42,12 +52,14 @@ android:text="@string/account_settings_example_jabber_id" android:textColor="@color/black87" android:textSize="?attr/TextSizeHeadline" - android:textStyle="bold" /> + android:textStyle="bold" + android:layout_gravity="center_horizontal" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:orientation="vertical" > + android:orientation="vertical" + android:layout_gravity="center_horizontal"> <LinearLayout android:id="@+id/tags" @@ -55,7 +67,8 @@ android:layout_height="wrap_content" android:layout_marginTop="4dp" android:layout_marginBottom="4dp" - android:orientation="horizontal"> + android:orientation="horizontal" + android:layout_gravity="center_horizontal"> </LinearLayout> <TextView @@ -63,7 +76,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/black54" - android:textSize="?attr/TextSizeBody" /> + android:textSize="?attr/TextSizeBody" + android:layout_gravity="center_horizontal" /> </LinearLayout> <Button @@ -71,7 +85,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" - android:text="@string/add_contact"/> + android:text="@string/add_contact" + android:layout_gravity="center_horizontal" /> <CheckBox android:id="@+id/details_send_presence" diff --git a/src/main/res/layout/activity_edit_account.xml b/src/main/res/layout/activity_edit_account.xml index 8d60f5f1c..c803dd20f 100644 --- a/src/main/res/layout/activity_edit_account.xml +++ b/src/main/res/layout/activity_edit_account.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@color/grey200"> + xmlns:tools="http://schemas.android.com/tools" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/grey200"> <ScrollView android:layout_width="fill_parent" @@ -31,86 +31,92 @@ android:padding="@dimen/infocard_padding"> <com.makeramen.roundedimageview.RoundedImageView android:id="@+id/avater" - android:layout_width="72dp" - android:layout_height="72dp" - android:layout_alignParentTop="true" - android:layout_marginRight="16dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:maxHeight="384dp" + android:maxWidth="384dp" android:contentDescription="@string/account_image_description" - app:riv_corner_radius="2dp"/> + app:riv_corner_radius="2dp" + android:background="@drawable/message_border" + android:padding="1dp" + android:adjustViewBounds="true" + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + android:layout_marginBottom="16dp" /> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:layout_toRightOf="@+id/avater"> - <TextView - android:id="@+id/account_jid_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/account_settings_jabber_id" - android:textColor="@color/black87" - android:textSize="?attr/TextSizeBody" /> - - <AutoCompleteTextView - android:id="@+id/account_jid" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:hint="@string/account_settings_example_jabber_id" - android:inputType="textEmailAddress" - android:textColor="@color/black87" - android:textColorHint="@color/black54" - android:textSize="?attr/TextSizeBody" /> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:text="@string/account_settings_password" - android:textColor="@color/black87" - android:textSize="?attr/TextSizeBody" /> - - <EditText - android:id="@+id/account_password" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:hint="@string/password" - android:inputType="textPassword" - android:textColor="@color/black87" - android:textColorHint="@color/black54" - android:textSize="?attr/TextSizeBody" /> + android:layout_below="@+id/avater"> + <TextView + android:id="@+id/account_jid_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/account_settings_jabber_id" + android:textColor="@color/black87" + android:textSize="?attr/TextSizeBody" /> - <CheckBox - android:id="@+id/account_register_new" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:text="@string/register_account" - android:textColor="@color/black87" - android:textSize="?attr/TextSizeBody" /> + <AutoCompleteTextView + android:id="@+id/account_jid" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/account_settings_example_jabber_id" + android:inputType="textEmailAddress" + android:textColor="@color/black87" + android:textColorHint="@color/black54" + android:textSize="?attr/TextSizeBody" /> - <TextView - android:id="@+id/account_confirm_password_desc" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/account_settings_confirm_password" - android:textColor="@color/black87" - android:textSize="?attr/TextSizeBody" - android:visibility="gone" /> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:text="@string/account_settings_password" + android:textColor="@color/black87" + android:textSize="?attr/TextSizeBody" /> + + <EditText + android:id="@+id/account_password" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/password" + android:inputType="textPassword" + android:textColor="@color/black87" + android:textColorHint="@color/black54" + android:textSize="?attr/TextSizeBody" /> + + <CheckBox + android:id="@+id/account_register_new" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:text="@string/register_account" + android:textColor="@color/black87" + android:textSize="?attr/TextSizeBody" /> - <EditText - android:id="@+id/account_password_confirm" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:hint="@string/confirm_password" - android:inputType="textPassword" - android:visibility="gone" - android:textColor="@color/black87" - android:textColorHint="@color/black54" - android:textSize="?attr/TextSizeBody" /> + <TextView + android:id="@+id/account_confirm_password_desc" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/account_settings_confirm_password" + android:textColor="@color/black87" + android:textSize="?attr/TextSizeBody" + android:visibility="gone" /> + + <EditText + android:id="@+id/account_password_confirm" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:hint="@string/confirm_password" + android:inputType="textPassword" + android:visibility="gone" + android:textColor="@color/black87" + android:textColorHint="@color/black54" + android:textSize="?attr/TextSizeBody" /> </LinearLayout> </RelativeLayout> - <LinearLayout + <LinearLayout android:id="@+id/stats" android:layout_width="fill_parent" android:layout_height="fill_parent" @@ -368,65 +374,65 @@ android:visibility="visible" android:contentDescription="@string/copy_otr_clipboard_description"/> </RelativeLayout> - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:id="@+id/axolotl_fingerprint_box" - android:layout_marginTop="32dp"> - - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentLeft="true" - android:layout_toLeftOf="@+id/axolotl_actions" - android:orientation="vertical"> - - <TextView - android:id="@+id/axolotl_fingerprint" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textColor="@color/black87" - android:textSize="?attr/TextSizeBody" - android:typeface="monospace" - android:fontFamily="monospace"/> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textColor="@color/black54" - android:textSize="?attr/TextSizeInfo" - android:text="@string/this_device_omemo_fingerprint"/> - </LinearLayout> - - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/axolotl_actions" - android:layout_centerVertical="true" - android:layout_alignParentRight="true" - android:orientation="vertical"> - - <ImageButton - android:id="@+id/action_copy_axolotl_to_clipboard" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:background="?android:selectableItemBackground" - android:padding="@dimen/image_button_padding" - android:src="?attr/icon_copy" - android:visibility="visible" - android:contentDescription="@string/copy_omemo_clipboard_description"/> - <ImageButton - android:id="@+id/action_regenerate_axolotl_key" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:background="?android:selectableItemBackground" - android:padding="@dimen/image_button_padding" - android:src="?attr/icon_refresh" - android:visibility="gone" - android:contentDescription="@string/regenerate_omemo_key"/> - - </LinearLayout> - </RelativeLayout> + <RelativeLayout + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:id="@+id/axolotl_fingerprint_box" + android:layout_marginTop="32dp"> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_toLeftOf="@+id/axolotl_actions" + android:orientation="vertical"> + + <TextView + android:id="@+id/axolotl_fingerprint" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@color/black87" + android:textSize="?attr/TextSizeBody" + android:typeface="monospace" + android:fontFamily="monospace"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@color/black54" + android:textSize="?attr/TextSizeInfo" + android:text="@string/this_device_omemo_fingerprint"/> + </LinearLayout> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/axolotl_actions" + android:layout_centerVertical="true" + android:layout_alignParentRight="true" + android:orientation="vertical"> + + <ImageButton + android:id="@+id/action_copy_axolotl_to_clipboard" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="?android:selectableItemBackground" + android:padding="@dimen/image_button_padding" + android:src="?attr/icon_copy" + android:visibility="visible" + android:contentDescription="@string/copy_omemo_clipboard_description"/> + <ImageButton + android:id="@+id/action_regenerate_omemo_key" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="?android:selectableItemBackground" + android:padding="@dimen/image_button_padding" + android:src="?attr/icon_refresh" + android:visibility="gone" + android:contentDescription="@string/regenerate_omemo_key"/> + + </LinearLayout> + </RelativeLayout> </LinearLayout> <LinearLayout android:id="@+id/other_device_keys_card" diff --git a/src/main/res/layout/activity_muc_details.xml b/src/main/res/layout/activity_muc_details.xml index 5f9424a9f..1ff6f95d2 100644 --- a/src/main/res/layout/activity_muc_details.xml +++ b/src/main/res/layout/activity_muc_details.xml @@ -1,185 +1,187 @@ <?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:background="@color/grey200"> - - <LinearLayout - android:id="@+id/muc_main_layout" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> - - <LinearLayout - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_marginLeft="@dimen/activity_horizontal_margin" - android:layout_marginRight="@dimen/activity_horizontal_margin" - android:layout_marginTop="@dimen/activity_vertical_margin" - android:layout_marginBottom="@dimen/activity_vertical_margin" - android:background="@drawable/infocard_border" - android:orientation="vertical" - android:padding="@dimen/infocard_padding"> - - <TextView - android:id="@+id/muc_jabberid" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginBottom="16dp" - android:text="@string/account_settings_example_jabber_id" - android:textColor="@color/black87" - android:textSize="?attr/TextSizeHeadline" - android:textStyle="bold"/> - - <RelativeLayout - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="32dp"> - - <com.makeramen.roundedimageview.RoundedImageView - android:id="@+id/your_photo" - android:layout_width="48dp" - android:layout_height="48dp" - android:layout_alignParentLeft="true" - android:src="@drawable/ic_profile" - app:riv_corner_radius="2dp" /> - - <LinearLayout - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:layout_toRightOf="@+id/your_photo" - android:orientation="vertical" - android:paddingLeft="8dp"> - - <TextView - android:id="@+id/muc_your_nick" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:textColor="@color/black87" - android:textSize="?attr/TextSizeHeadline"/> - - <TextView - android:id="@+id/muc_role" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:textColor="@color/black87" - android:textSize="?attr/TextSizeBody"/> - </LinearLayout> - - <ImageButton - android:id="@+id/edit_nick_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_centerVertical="true" - android:background="?android:selectableItemBackground" - android:padding="@dimen/image_button_padding" - android:src="?attr/icon_edit_dark"/> - </RelativeLayout> - - <RelativeLayout - android:layout_width="fill_parent" - android:layout_height="wrap_content"> - <TextView - android:id="@+id/muc_conference_type" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/private_conference" - android:layout_centerVertical="true" - android:textColor="@color/black87" - android:textSize="?attr/TextSizeBody" - android:layout_alignParentLeft="true" - android:layout_toLeftOf="@+id/change_conference_button" - /> - <ImageButton - android:id="@+id/change_conference_button" - style="?android:attr/buttonStyleSmall" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:layout_alignParentRight="true" - android:layout_centerVertical="true" - android:background="?android:selectableItemBackground" - android:padding="@dimen/image_button_padding" - android:src="?attr/icon_settings"/> - </RelativeLayout> - - <TableLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:id="@+id/muc_info_more" - android:stretchColumns="1" - android:visibility="gone"> - - <TableRow - android:layout_width="fill_parent" - android:layout_height="match_parent"> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/server_info_mam" - android:textColor="@color/black87" - android:textSize="?attr/TextSizeBody" /> - - <TextView - android:id="@+id/muc_info_mam" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="right" - android:textColor="@color/black87" - android:textSize="?attr/TextSizeBody" /> - </TableRow> - - </TableLayout> - - <TextView - android:id="@+id/details_account" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="right" - android:layout_marginTop="32dp" - android:text="@string/using_account" - android:textColor="@color/black54" - android:textSize="?attr/TextSizeInfo"/> - </LinearLayout> - - <LinearLayout - android:id="@+id/muc_more_details" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_marginLeft="@dimen/activity_horizontal_margin" - android:layout_marginRight="@dimen/activity_horizontal_margin" - android:layout_marginTop="@dimen/activity_vertical_margin" - android:layout_marginBottom="@dimen/activity_vertical_margin" - android:background="@drawable/infocard_border" - android:orientation="vertical" - android:padding="@dimen/infocard_padding"> - - - <LinearLayout - android:id="@+id/muc_members" - android:layout_width="fill_parent" - android:layout_height="0dp" - android:layout_weight="1" - android:divider="?android:dividerHorizontal" - android:orientation="vertical" - android:showDividers="middle"> - </LinearLayout> - - <Button - android:id="@+id/invite" - style="?android:attr/buttonStyleSmall" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:layout_marginTop="24dp" - android:text="@string/invite_contact"/> - </LinearLayout> - - </LinearLayout> + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="@color/grey200"> + + <LinearLayout + android:id="@+id/muc_main_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/activity_vertical_margin" + android:layout_marginLeft="@dimen/activity_horizontal_margin" + android:layout_marginRight="@dimen/activity_horizontal_margin" + android:layout_marginTop="@dimen/activity_vertical_margin" + android:background="@drawable/infocard_border" + android:orientation="vertical" + android:padding="@dimen/infocard_padding"> + + <TextView + android:id="@+id/muc_jabberid" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="16dp" + android:text="@string/account_settings_example_jabber_id" + android:textColor="@color/black87" + android:textSize="?attr/TextSizeHeadline" + android:textStyle="bold" /> + + <RelativeLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="32dp"> + + <com.makeramen.roundedimageview.RoundedImageView + android:id="@+id/your_photo" + android:layout_width="72dp" + android:layout_height="72dp" + android:layout_alignParentEnd="false" + android:layout_alignParentLeft="true" + android:background="@drawable/message_border" + android:padding="1dp" + android:src="@drawable/ic_profile" + app:riv_corner_radius="2dp" /> + + <LinearLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_toRightOf="@+id/your_photo" + android:orientation="vertical" + android:paddingLeft="8dp"> + + <TextView + android:id="@+id/muc_your_nick" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:textColor="@color/black87" + android:textSize="?attr/TextSizeHeadline" /> + + <TextView + android:id="@+id/muc_role" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:textColor="@color/black87" + android:textSize="?attr/TextSizeBody" /> + </LinearLayout> + + <ImageButton + android:id="@+id/edit_nick_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_centerVertical="true" + android:background="?android:selectableItemBackground" + android:padding="@dimen/image_button_padding" + android:src="?attr/icon_edit_dark" /> + </RelativeLayout> + + <RelativeLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content"> + + <TextView + android:id="@+id/muc_conference_type" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_centerVertical="true" + android:layout_toLeftOf="@+id/change_conference_button" + android:text="@string/private_conference" + android:textColor="@color/black87" + android:textSize="?attr/TextSizeBody" /> + + <ImageButton + android:id="@+id/change_conference_button" + style="?android:attr/buttonStyleSmall" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_centerVertical="true" + android:layout_gravity="center_horizontal" + android:background="?android:selectableItemBackground" + android:padding="@dimen/image_button_padding" + android:src="?attr/icon_settings" /> + </RelativeLayout> + <TableLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:id="@+id/muc_info_more" + android:stretchColumns="1" + android:visibility="gone"> + + <TableRow + android:layout_width="fill_parent" + android:layout_height="match_parent"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/server_info_mam" + android:textColor="@color/black87" + android:textSize="?attr/TextSizeBody" /> + + <TextView + android:id="@+id/muc_info_mam" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="right" + android:textColor="@color/black87" + android:textSize="?attr/TextSizeBody" /> + </TableRow> + + </TableLayout> + + <TextView + android:id="@+id/details_account" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="right" + android:layout_marginTop="32dp" + android:text="@string/using_account" + android:textColor="@color/black54" + android:textSize="?attr/TextSizeInfo" /> + </LinearLayout> + + <LinearLayout + android:id="@+id/muc_more_details" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/activity_vertical_margin" + android:layout_marginLeft="@dimen/activity_horizontal_margin" + android:layout_marginRight="@dimen/activity_horizontal_margin" + android:layout_marginTop="@dimen/activity_vertical_margin" + android:background="@drawable/infocard_border" + android:orientation="vertical" + android:padding="@dimen/infocard_padding"> + + + <LinearLayout + android:id="@+id/muc_members" + android:layout_width="fill_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:divider="?android:dividerHorizontal" + android:orientation="vertical" + android:showDividers="middle"></LinearLayout> + + <Button + android:id="@+id/invite" + style="?android:attr/buttonStyleSmall" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:layout_marginTop="24dp" + android:text="@string/invite_contact" /> + </LinearLayout> + + </LinearLayout> </ScrollView>
\ No newline at end of file diff --git a/src/main/res/layout/activity_publish_profile_picture.xml b/src/main/res/layout/activity_publish_profile_picture.xml index 3b249a665..e5113dab9 100644 --- a/src/main/res/layout/activity_publish_profile_picture.xml +++ b/src/main/res/layout/activity_publish_profile_picture.xml @@ -1,43 +1,107 @@ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/grey50" > - + android:background="@color/grey200"> + <ScrollView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_above="@+id/button_bar" + android:layout_alignParentTop="true" > <LinearLayout - android:id="@+id/account_image_wrapper" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_alignParentTop="true" - android:layout_centerHorizontal="true" - android:layout_marginBottom="8dp" - android:layout_marginTop="24dp" - android:background="@drawable/account_image_border" > - - <ImageView - android:id="@+id/account_image" - android:layout_width="194dp" - android:layout_height="194dp" /> - </LinearLayout> + android:orientation="vertical" + android:gravity="center_horizontal"> - <TextView - android:id="@+id/hint" - android:layout_width="wrap_content" + <LinearLayout + android:layout_width="fill_parent" android:layout_height="wrap_content" - android:layout_below="@id/account_image_wrapper" - android:layout_centerHorizontal="true" - android:text="@string/touch_to_choose_picture" - android:textColor="@color/black54" /> + android:layout_above="@+id/button_bar" + android:layout_alignParentLeft="true" + android:layout_alignParentRight="true" + android:layout_below="@+id/secondary_hint" + android:gravity="center_vertical" + android:orientation="vertical" + android:paddingLeft="8dp" + android:paddingRight="8dp" + android:layout_marginRight="@dimen/activity_horizontal_margin" + android:layout_marginLeft="@dimen/activity_horizontal_margin" + android:layout_marginTop="@dimen/activity_vertical_margin" + android:layout_marginBottom="@dimen/activity_vertical_margin" + android:background="@drawable/infocard_border"> - <TextView - android:id="@+id/secondary_hint" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@id/hint" - android:layout_centerHorizontal="true" - android:text="@string/or_long_press_for_default" - android:textColor="@color/black54" /> + <LinearLayout + android:id="@+id/account_image_wrapper" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + android:layout_marginBottom="8dp" + android:layout_marginTop="24dp" + android:background="@drawable/account_image_border" + android:layout_gravity="center_horizontal"> + + <ImageView + android:id="@+id/account_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:maxHeight="384dp" + android:maxWidth="384dp" + android:adjustViewBounds="true" + android:padding="1dp" + android:background="@drawable/message_border" /> + </LinearLayout> + + <TextView + android:id="@+id/hint" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/account_image_wrapper" + android:layout_centerHorizontal="true" + android:text="@string/touch_to_choose_picture" + android:textColor="@color/black54" + android:layout_marginLeft="@dimen/activity_horizontal_margin" + android:layout_marginRight="@dimen/activity_horizontal_margin" + android:layout_marginTop="@dimen/activity_vertical_margin" + android:layout_marginBottom="@dimen/activity_vertical_margin" + android:layout_gravity="center_horizontal" /> + + <TextView + android:id="@+id/secondary_hint" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/hint" + android:layout_centerHorizontal="true" + android:text="@string/or_long_press_for_default" + android:textColor="@color/black54" + android:layout_marginBottom="@dimen/activity_vertical_margin" + android:layout_marginTop="@dimen/activity_vertical_margin" + android:layout_marginLeft="@dimen/activity_horizontal_margin" + android:layout_marginRight="@dimen/activity_horizontal_margin" + android:layout_gravity="center_horizontal" /> + + <TextView + android:id="@+id/account" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@color/black87" + android:textSize="?attr/TextSizeHeadline" /> + <TextView + android:id="@+id/hint_or_warning" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:minLines="3" + android:text="@string/publish_avatar_explanation" + android:textColor="@color/black87" + android:textSize="?attr/TextSizeBody" /> + </LinearLayout> +</LinearLayout> + </ScrollView> <LinearLayout android:id="@+id/button_bar" android:layout_width="wrap_content" @@ -73,34 +137,4 @@ android:textColor="@color/black54" /> </LinearLayout> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="fill_parent" - android:layout_above="@+id/button_bar" - android:layout_alignParentLeft="true" - android:layout_alignParentRight="true" - android:layout_below="@+id/secondary_hint" - android:gravity="center_vertical" - android:orientation="vertical" - android:paddingLeft="8dp" - android:paddingRight="8dp" > - - <TextView - android:id="@+id/account" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textColor="@color/black87" - android:textSize="?attr/TextSizeHeadline" /> - - <TextView - android:id="@+id/hint_or_warning" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:minLines="3" - android:text="@string/publish_avatar_explanation" - android:textColor="@color/black87" - android:textSize="?attr/TextSizeBody" /> - </LinearLayout> - -</RelativeLayout>
\ No newline at end of file +</RelativeLayout> diff --git a/src/main/res/layout/activity_updater.xml b/src/main/res/layout/activity_updater.xml new file mode 100644 index 000000000..50bdf0695 --- /dev/null +++ b/src/main/res/layout/activity_updater.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context="eu.siacs.conversations.ui.UpdaterActivity"> + + <TextView android:id="@+id/updater" android:layout_width="wrap_content" + android:layout_height="wrap_content" android:layout_alignParentLeft="true" + android:layout_alignParentTop="true" android:layout_marginTop="6dp" + android:text="Current" android:textAppearance="?android:attr/textAppearanceMedium" + android:textStyle="normal" + android:layout_marginLeft="4dp" + android:layout_marginRight="4dp" /> + +</RelativeLayout> diff --git a/src/main/res/layout/contact.xml b/src/main/res/layout/contact.xml index 56443c346..653bb9176 100644 --- a/src/main/res/layout/contact.xml +++ b/src/main/res/layout/contact.xml @@ -8,11 +8,12 @@ <com.makeramen.roundedimageview.RoundedImageView android:id="@+id/contact_photo" - android:layout_width="48dp" - android:layout_height="48dp" + android:layout_width="56dp" + android:layout_height="56dp" android:layout_alignParentLeft="true" - android:scaleType="centerCrop" android:src="@drawable/ic_profile" + android:background="@drawable/message_border" + android:padding="1dp" app:riv_corner_radius="2dp" /> <LinearLayout diff --git a/src/main/res/layout/conversation_list_row.xml b/src/main/res/layout/conversation_list_row.xml index 7321b0f66..575647f0a 100644 --- a/src/main/res/layout/conversation_list_row.xml +++ b/src/main/res/layout/conversation_list_row.xml @@ -28,6 +28,8 @@ android:layout_height="56dp" android:layout_alignParentLeft="true" android:scaleType="centerCrop" + android:background="@drawable/message_border" + android:padding="1dp" app:riv_corner_radius="2dp" /> <RelativeLayout @@ -68,7 +70,7 @@ <ImageView android:id="@+id/conversation_lastimage" android:layout_width="fill_parent" - android:layout_height="36dp" + android:layout_height="14dp" android:background="@color/black87" android:scaleType="centerCrop" /> </LinearLayout> diff --git a/src/main/res/layout/message_received.xml b/src/main/res/layout/message_received.xml index a998bf37e..7d4a0993b 100644 --- a/src/main/res/layout/message_received.xml +++ b/src/main/res/layout/message_received.xml @@ -17,6 +17,8 @@ android:layout_alignParentTop="true" android:scaleType="fitXY" android:src="@drawable/ic_profile" + android:background="@drawable/message_border" + android:padding="1dp" app:riv_corner_radius="2dp" /> <LinearLayout @@ -105,5 +107,4 @@ </LinearLayout> </LinearLayout> </LinearLayout> - </RelativeLayout>
\ No newline at end of file diff --git a/src/main/res/layout/message_sent.xml b/src/main/res/layout/message_sent.xml index 55f874e65..48fb5ff4d 100644 --- a/src/main/res/layout/message_sent.xml +++ b/src/main/res/layout/message_sent.xml @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - android:layout_width="fill_parent" - android:layout_height="wrap_content" + android:layout_width="match_parent" + android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="3dp" android:paddingLeft="8dp" @@ -14,7 +14,8 @@ android:layout_width="48dp" android:layout_height="48dp" android:scaleType="fitXY" - android:paddingBottom="3dp" + android:background="@drawable/message_border" + android:padding="1dp" android:src="@drawable/ic_profile" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" @@ -99,11 +100,18 @@ android:layout_height="?attr/TextSizeInfo" android:layout_gravity="center_vertical" android:layout_marginLeft="4sp" - android:alpha="0.54" android:gravity="center_vertical" android:src="@drawable/ic_received_indicator" /> + <ImageView + android:id="@+id/indicator_read" + android:layout_width="?attr/TextSizeInfo" + android:layout_height="?attr/TextSizeInfo" + android:layout_gravity="center_vertical" + android:gravity="center_vertical" + android:src="@drawable/ic_read_indicator" + android:layout_marginLeft="-8sp" + android:adjustViewBounds="false" /> </LinearLayout> </LinearLayout> </LinearLayout> - </RelativeLayout>
\ No newline at end of file diff --git a/src/main/res/layout/message_status.xml b/src/main/res/layout/message_status.xml index ad2579faa..2f14cbf54 100644 --- a/src/main/res/layout/message_status.xml +++ b/src/main/res/layout/message_status.xml @@ -16,9 +16,10 @@ android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_marginRight="-1.5dp" - android:padding="0dp" + android:padding="1dp" android:scaleType="fitXY" android:src="@drawable/ic_profile" + android:background="@drawable/message_border" app:riv_corner_radius="1dp"/> <TextView diff --git a/src/main/res/menu/conversations.xml b/src/main/res/menu/conversations.xml index 4c724d27d..47214a6b7 100644 --- a/src/main/res/menu/conversations.xml +++ b/src/main/res/menu/conversations.xml @@ -20,14 +20,15 @@ android:title="@string/attach_file"/> <item android:id="@+id/action_contact_details" + android:icon="?attr/icon_group" android:orderInCategory="40" - android:showAsAction="never" + android:showAsAction="always" android:title="@string/action_contact_details"/> <item android:id="@+id/action_muc_details" android:icon="?attr/icon_group" android:orderInCategory="40" - android:showAsAction="ifRoom" + android:showAsAction="always" android:title="@string/action_muc_details"/> <item android:id="@+id/action_invite" @@ -66,5 +67,10 @@ android:orderInCategory="100" android:showAsAction="never" android:title="@string/action_settings"/> + <item + android:id="@+id/action_check_updates" + android:orderInCategory="100" + android:showAsAction="never" + android:title="@string/action_check_update"/> </menu>
\ No newline at end of file diff --git a/src/main/res/menu/menu_updater.xml b/src/main/res/menu/menu_updater.xml new file mode 100644 index 000000000..f52657372 --- /dev/null +++ b/src/main/res/menu/menu_updater.xml @@ -0,0 +1,7 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + tools:context="eu.siacs.conversations.ui.UpdaterActivity"> + <item android:id="@+id/action_settings" android:title="@string/action_settings" + android:orderInCategory="100" app:showAsAction="never" /> +</menu> diff --git a/src/main/res/menu/muc_details_context.xml b/src/main/res/menu/muc_details_context.xml index af5e691b7..4a8b13d16 100644 --- a/src/main/res/menu/muc_details_context.xml +++ b/src/main/res/menu/muc_details_context.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> - <item + <item android:id="@+id/start_conversation" android:title="@string/start_conversation" android:visible="false"/> diff --git a/src/main/res/values-bg/strings.xml b/src/main/res/values-bg/strings.xml index 030432ad6..11c3446b9 100644 --- a/src/main/res/values-bg/strings.xml +++ b/src/main/res/values-bg/strings.xml @@ -443,6 +443,7 @@ <string name="hide_offline">Скриване на тези извън линия</string> <string name="disable_account">Деактивиране на профила</string> <string name="contact_is_typing">%s пише...</string> + <string name="is_typing">пише...</string> <string name="contact_has_stopped_typing">%s спря да пише</string> <string name="pref_chat_states">Известия за писането</string> <string name="pref_chat_states_summary">Позволяване на контакта Ви да вижда, когато пишете ново съобщение</string> diff --git a/src/main/res/values-ca/strings.xml b/src/main/res/values-ca/strings.xml index 806ed7a03..43db6f5fb 100644 --- a/src/main/res/values-ca/strings.xml +++ b/src/main/res/values-ca/strings.xml @@ -407,6 +407,7 @@ <string name="hide_offline">Amaga el fora de línia</string> <string name="disable_account">Deshabilita el compte</string> <string name="contact_is_typing">%s està escrivint...</string> + <string name="is_typing">està escrivint...</string> <string name="contact_has_stopped_typing">%s ha deixat d\'escriure</string> <string name="pref_chat_states">Notificacions d\'escriptura</string> <string name="pref_chat_states_summary">Permet el teu contacte saber quan estàs escrivint un missatge nou</string> diff --git a/src/main/res/values-cs/strings.xml b/src/main/res/values-cs/strings.xml index 0c5dca552..57f93f5cd 100644 --- a/src/main/res/values-cs/strings.xml +++ b/src/main/res/values-cs/strings.xml @@ -439,6 +439,7 @@ <string name="hide_offline">Skrýt offline</string> <string name="disable_account">Vypnout účet</string> <string name="contact_is_typing">%s píše...</string> + <string name="is_typing">píše...</string> <string name="contact_has_stopped_typing">%s přestal(a) psát</string> <string name="pref_chat_states">Upozornění při psaní</string> <string name="pref_chat_states_summary">Oznamovat kontaktům že píšete novou zprávu</string> diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index b7cc4440f..6b8e08657 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -1,4 +1,4 @@ -<?xml version='1.0' encoding='UTF-8'?> +<?xml version='1.0' encoding='UTF-8'?> <resources> <string name="action_settings">Einstellungen</string> <string name="action_add">Neue Unterhaltung</string> @@ -76,12 +76,12 @@ <string name="delete_messages">Nachrichten löschen</string> <string name="also_end_conversation">Diese Unterhaltung danach beenden</string> <string name="choose_presence">Ressource des Kontakts auswählen</string> - <string name="send_unencrypted_message">Unverschlüsselt schreiben…</string> + <string name="send_unencrypted_message">Normal schreiben…</string> <string name="send_otr_message">OTR-verschlüsselt schreiben…</string> <string name="send_omemo_message">OMEMO-verschlüsselt schreiben…</string> <string name="send_pgp_message">OpenPGP-verschlüsselt schreiben…</string> <string name="your_nick_has_been_changed">Dein Nickname wurde geändert</string> - <string name="send_unencrypted">Unverschlüsselt senden</string> + <string name="send_unencrypted">Normal verschicken</string> <string name="decryption_failed">Entschlüsselung fehlgeschlagen. Vielleicht hast du nicht den richtigen privaten Schlüssel.</string> <string name="openkeychain_required">OpenKeychain</string> <string name="openkeychain_required_long">Conversations benutzt eine Drittanwendung namens <b>OpenKeychain</b>, um Nachrichten zu ver- und entschlüsseln und um deine Schlüssel zu verwalten.\n\nOpenKeychain ist GPLv3-lizenziert und kann über F-Droid oder Google Play bezogen werden.\n\n<small>(Bitte starte Conversations danach neu.)</small></string> @@ -212,7 +212,7 @@ <string name="this_device_omemo_fingerprint">Eigener OMEMO-Fingerabdruck</string> <string name="other_devices">Andere Geräte</string> <string name="trust_omemo_fingerprints">OMEMO-Fingerabdruck vertrauen</string> - <string name="fetching_keys">Schlüssel abrufen...</string> + <string name="fetching_keys">Schlüssel abrufen…</string> <string name="done">Erledigt</string> <string name="verify">Überprüfen</string> <string name="decrypt">Entschlüsseln</string> @@ -335,12 +335,9 @@ <string name="conversations_foreground_service">Conversations</string> <string name="pref_keep_foreground_service">Den Dienst im Vordergrund ausführen.</string> <string name="pref_keep_foreground_service_summary">Verhindert, dass Android Conversations beendet und die Verbindung unterbricht</string> - <string name="pref_export_logs">Chats exportieren</string> - <string name="pref_export_logs_summary">Chats auf SD-Karte schreiben</string> - <string name="notification_export_logs_title">Chats auf SD-Karte schreiben</string> <string name="choose_file">Datei auswählen</string> - <string name="receiving_x_file">Empfange %1$s (%2$d%% abgeschlossen)</string> - <string name="download_x_file">%s wird heruntergeladen</string> + <string name="receiving_x_file">Empfange %1$s (%2$d%% abgeschlossen)</string> + <string name="download_x_file">%s herunterladen</string> <string name="file">Datei</string> <string name="open_x_file">%s öffnen</string> <string name="sending_file">Senden (%1$d%% abgeschlossen)</string> @@ -443,6 +440,7 @@ <string name="hide_offline">Offline verstecken</string> <string name="disable_account">Konto abschalten</string> <string name="contact_is_typing">%s schreibt…</string> + <string name="is_typing">schreibt…</string> <string name="contact_has_stopped_typing">%s schreibt nicht mehr</string> <string name="pref_chat_states">Tipp-Benachrichtigung</string> <string name="pref_chat_states_summary">Informiere deine Kontakte, wenn du eine Nachricht eintippst.</string> @@ -475,6 +473,7 @@ <string name="recently_used">zuletzt verwendet</string> <string name="choose_quick_action">wähle Schnell-Taste</string> <string name="search_for_contacts_or_groups">Nach Kontakten oder Konferenzen suchen</string> + <string name="elv_undo">Rückgängig</string> <string name="send_private_message">Private Nachricht senden</string> <string name="user_has_left_conference">%s hat die Konferenz verlassen!</string> <string name="username">Benutzername</string> @@ -485,8 +484,22 @@ <string name="download_failed_could_not_connect">Download fehlgeschlagen: keine Verbindung zum Host</string> <string name="pref_use_white_background">Weißen Hintergrund benutzen</string> <string name="pref_use_white_background_summary">Empfangene Nachrichten als schwarzen Text auf weißem Hintergrund anzeigen</string> + <string name="action_check_update">Auf Updates prüfen</string> + <string name="current_version">Installierte Version:</string> + <string name="new_version">Neue Version: </string> + <string name="remind_later">später</string> + <string name="checking_for_updates">Auf Conversations Updates prüfen</string> + <string name="title_activity_updater">Update Dienst</string> + <string name="update">Aktualisieren</string> + <string name="update_available">Conversations %1$s mit folgenden Änderungen ist verfügbar:\n\n%2$s\n\nMöchtest Du von Conversations %3$s auf Conversations %1$s aktualisieren?</string> + <string name="download_started">Download gestartet</string> + <string name="no_update_available">Kein Update verfügbar</string> <string name="account_status_dns_timeout">Zeitüberschreitung bei der Namensauflösung</string> <string name="server_info_broken">Fehlerhaft</string> + <string name="update_info">Conversation prüft auf eine neuere Version. Wenn ein Update verfügbar ist, wirst du gefragt, ob du deine Version aktualisieren möchtest. Der Update Dienst lädt und installiert die neue Version automatisch.</string> + <string name="notification_export_logs_title">Schreibe Chats auf SD Karte</string> + <string name="pref_export_logs">Exportiere Chats</string> + <string name="pref_export_logs_summary">Schreibe Chats auf SD Karte</string> <string name="pref_presence_settings">Status Einstellungen</string> <string name="pref_away_when_screen_off">Abwesend</string> <string name="pref_away_when_screen_off_summary">Setze deinen Status auf abwesend, wenn dein Bildschirm abgeschaltet ist</string> @@ -501,4 +514,5 @@ <string name="certificate_chain_is_not_trusted">Zertifikat wird nicht vertraut</string> <string name="jid_does_not_match_certificate">Jabber-ID stimmt nicht dem Zertifikat überein</string> <string name="action_renew_certificate">Zertifikat erneuern</string> + </resources> diff --git a/src/main/res/values-el/strings.xml b/src/main/res/values-el/strings.xml index ed38d0c06..ef1e9532b 100644 --- a/src/main/res/values-el/strings.xml +++ b/src/main/res/values-el/strings.xml @@ -407,6 +407,7 @@ <string name="hide_offline">Απόκρυψη των εκτός σύνδεσης</string> <string name="disable_account">Απενεργοποίηση λογαριασμού</string> <string name="contact_is_typing">Ο χρήστης %s γράφει...</string> + <string name="is_typing">Ο χρήστης γράφει...</string> <string name="contact_has_stopped_typing">Ο χρήστης %s σταμάτησε να γράφει</string> <string name="pref_chat_states">Ειδοποιήσεις πληκτρολόγησης</string> <string name="pref_chat_states_summary">Επιτρέψτε στην επαφή σας να γνωρίζει πότε γράφετε ένα νέο μήνυμα</string> diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index cdddfb164..0494225cd 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -443,6 +443,7 @@ <string name="hide_offline">Ocultar desconectados</string> <string name="disable_account">Deshabilitar Cuenta</string> <string name="contact_is_typing">%s está escribiendo...</string> + <string name="is_typing">está escribiendo...</string> <string name="contact_has_stopped_typing">%s ha dejado de escribir</string> <string name="pref_chat_states">Notificación de escritura</string> <string name="pref_chat_states_summary">Permite a tus contactos saber cuando estás escribiendo un nuevo mensaje</string> diff --git a/src/main/res/values-eu/strings.xml b/src/main/res/values-eu/strings.xml index 86f39b0f6..d3dfa995f 100644 --- a/src/main/res/values-eu/strings.xml +++ b/src/main/res/values-eu/strings.xml @@ -439,6 +439,7 @@ <string name="hide_offline">Lineaz kanpokoak ezkutatu</string> <string name="disable_account">Kontua ezgaitu</string> <string name="contact_is_typing">%s idazten ari da...</string> + <string name="is_typing">idazten ari da...</string> <string name="contact_has_stopped_typing">%s(e)k idazteari utzi dio</string> <string name="pref_chat_states">Idazketa jakinarazpenak</string> <string name="pref_chat_states_summary">Zure kontaktuak mezu berri bat noiz idazten ari zaren jakin dezan baimendu</string> diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index b6c5f0daf..90ce584db 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -439,6 +439,7 @@ <string name="hide_offline">Se cacher hors-ligne</string> <string name="disable_account">Désactiver le compte</string> <string name="contact_is_typing">%s écrit un message...</string> + <string name="is_typing">écrit un message...</string> <string name="contact_has_stopped_typing">%s a arrêté d\'écrire</string> <string name="pref_chat_states">Notifications d\'écriture</string> <string name="pref_chat_states_summary">Permettre à votre contact de savoir que vous écrivez un message</string> diff --git a/src/main/res/values-id/strings.xml b/src/main/res/values-id/strings.xml index 894c18a71..6c22c1323 100644 --- a/src/main/res/values-id/strings.xml +++ b/src/main/res/values-id/strings.xml @@ -412,6 +412,7 @@ <string name="hide_offline">Sembunyikan Offline</string> <string name="disable_account">Nonaktifkan Akun</string> <string name="contact_is_typing">%s sedang mengetik...</string> + <string name="is_typing">sedang mengetik...</string> <string name="contact_has_stopped_typing">%s telah berhenti mengetik</string> <string name="pref_chat_states">Notifikasi ketik pesan</string> <string name="pref_chat_states_summary">Biarkan kontak Anda tahu ketika Anda sedang menulis pesan baru</string> diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml index 97311427c..31d4a29ef 100644 --- a/src/main/res/values-it/strings.xml +++ b/src/main/res/values-it/strings.xml @@ -408,6 +408,7 @@ <string name="hide_offline">Nascondi i contatti offline</string> <string name="disable_account">Disabilita l\'account</string> <string name="contact_is_typing">%s sta digitando...</string> + <string name="is_typing">sta digitando...</string> <string name="contact_has_stopped_typing">%s ha smesso di digitare</string> <string name="pref_chat_states_summary">Permetti al tuo contatto di vedere quando stai digitando</string> <string name="send_location">Invia la posizione</string> diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml index 45c67940e..c30c87ff2 100644 --- a/src/main/res/values-ja/strings.xml +++ b/src/main/res/values-ja/strings.xml @@ -439,6 +439,7 @@ <string name="hide_offline">オフラインを非表示にする</string> <string name="disable_account">アカウントを無効にする</string> <string name="contact_is_typing">%s は入力中...</string> + <string name="is_typing">は入力中...</string> <string name="contact_has_stopped_typing">%s は入力を停止しました</string> <string name="pref_chat_states">入力中通知</string> <string name="pref_chat_states_summary">あなたが新しいメッセージを書いている時に、連絡先に知らせます</string> diff --git a/src/main/res/values-ko/strings.xml b/src/main/res/values-ko/strings.xml index 6f654dcf7..6566724b0 100644 --- a/src/main/res/values-ko/strings.xml +++ b/src/main/res/values-ko/strings.xml @@ -407,6 +407,7 @@ <string name="hide_offline">오프라인 숨기기 </string> <string name="disable_account">계정 해제 </string> <string name="contact_is_typing">%s 이(가) 입력중입니다... </string> + <string name="is_typing">이(가) 입력중입니다... </string> <string name="contact_has_stopped_typing">%s 이(가) 입력을 중단했습니다 </string> <string name="pref_chat_states">입력 알림 </string> <string name="pref_chat_states_summary">새 메세지를 작성할 때 이를 연락처에게 알립니다 </string> diff --git a/src/main/res/values-ms/strings.xml b/src/main/res/values-ms/strings.xml new file mode 100644 index 000000000..c757504ac --- /dev/null +++ b/src/main/res/values-ms/strings.xml @@ -0,0 +1,2 @@ +<?xml version='1.0' encoding='UTF-8'?> +<resources/> diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index 61d57b260..1507865bf 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -439,6 +439,7 @@ <string name="hide_offline">Offline contacten verbergen</string> <string name="disable_account">Account uitschakelen</string> <string name="contact_is_typing">%s is aan het typen...</string> + <string name="is_typing">is aan het typen...</string> <string name="contact_has_stopped_typing">%s is gestopt met typen</string> <string name="pref_chat_states">Aan-het-typen-meldingen</string> <string name="pref_chat_states_summary">Laat je contacten weten wanneer je een nieuw bericht aan het schrijven bent</string> diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index 408672ca1..16df1b3b3 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -439,6 +439,7 @@ <string name="hide_offline">Ukryj niedostępnych</string> <string name="disable_account">Wyłącz konto</string> <string name="contact_is_typing">%s pisze...</string> + <string name="is_typing">pisze...</string> <string name="contact_has_stopped_typing">%s przestał(a) pisać</string> <string name="pref_chat_states">Powiadomienia pisania</string> <string name="pref_chat_states_summary">Powiadamiaj rozmówcę, kiedy rozpoczynasz nową wiadomość</string> diff --git a/src/main/res/values-pt/strings.xml b/src/main/res/values-pt/strings.xml index 57f9d7c6e..c0614d88f 100644 --- a/src/main/res/values-pt/strings.xml +++ b/src/main/res/values-pt/strings.xml @@ -282,6 +282,7 @@ <string name="sending_x_file">Enviando %s</string> <string name="offering_x_file">Oferecendo %s</string> <string name="contact_is_typing">%s está digitando...</string> + <string name="is_typing">está digitando...</string> <string name="contact_has_stopped_typing">%s parou de digitar</string> <string name="pref_chat_states">Notificações de digitação</string> <string name="send_location">Enviar localização</string> diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index 4c5c205fe..54ea80bc8 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -421,6 +421,7 @@ <string name="hide_offline">Скрыть пользователей вне сети</string> <string name="disable_account">Отключить учётную запись</string> <string name="contact_is_typing">%s набирает сообщение...</string> + <string name="is_typing">набирает сообщение...</string> <string name="contact_has_stopped_typing">%s прекратил набор</string> <string name="pref_chat_states">Оповещения о наборе</string> <string name="pref_chat_states_summary">Позволяет вашим контактам видеть когда вы пишете новое сообщение</string> diff --git a/src/main/res/values-sk/strings.xml b/src/main/res/values-sk/strings.xml index b0f982c20..b8a3993e0 100644 --- a/src/main/res/values-sk/strings.xml +++ b/src/main/res/values-sk/strings.xml @@ -409,6 +409,7 @@ <string name="hide_offline">Skryť neprihlásených</string> <string name="disable_account">Vypnúť účet</string> <string name="contact_is_typing">%s píše...</string> + <string name="is_typing">píše...</string> <string name="contact_has_stopped_typing">%s prestal písať</string> <string name="pref_chat_states">Upozornenia pri písaní</string> <string name="pref_chat_states_summary">Upozorniť kontakt, keď píšete novú správu</string> diff --git a/src/main/res/values-sr/strings.xml b/src/main/res/values-sr/strings.xml index 5cf49475f..a251fbcd8 100644 --- a/src/main/res/values-sr/strings.xml +++ b/src/main/res/values-sr/strings.xml @@ -439,6 +439,7 @@ <string name="hide_offline">Сакриј неповезане</string> <string name="disable_account">Онемогући налог</string> <string name="contact_is_typing">%s куца...</string> + <string name="is_typing">куца...</string> <string name="contact_has_stopped_typing">%s престаде да куца</string> <string name="pref_chat_states">Обавештења о куцању</string> <string name="pref_chat_states_summary">Обзнаните контакту кад куцате нову поруку</string> diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml index 90a0248ed..34a11698c 100644 --- a/src/main/res/values-sv/strings.xml +++ b/src/main/res/values-sv/strings.xml @@ -439,6 +439,7 @@ <string name="hide_offline">Dölj ej anslutna</string> <string name="disable_account">Deaktivera konton</string> <string name="contact_is_typing">%s skriver...</string> + <string name="is_typing">skriver...</string> <string name="contact_has_stopped_typing">%s har slutat skriva</string> <string name="pref_chat_states">Skriv-notifieringar</string> <string name="pref_chat_states_summary">Låter dina kontakter veta när du skriver ett nytt meddelande</string> diff --git a/src/main/res/values-w820dp/dimens.xml b/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 000000000..63fc81644 --- /dev/null +++ b/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,6 @@ +<resources> + <!-- Example customization of dimensions originally defined in res/values/dimens.xml + (such as screen margins) for screens with more than 820dp of available width. This + would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). --> + <dimen name="activity_horizontal_margin">64dp</dimen> +</resources> diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index 455d13a8b..8c41f1950 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -282,6 +282,8 @@ <string name="conference_members_only">此讨论组只允许成员加入</string> <string name="conference_kicked">你被从此讨论组踢出</string> <string name="using_account">用账户 %s</string> + <string name="checking_file">正在 HTTP 托管中检查图片</string> + <string name="image_file_deleted">此图片已经被删除</string> <string name="not_connected_try_again">你没有连接。请稍后重试</string> <string name="message_options">消息选项</string> <string name="copy_text">拷贝文本</string> diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 7ed0d3869..44e038bea 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name" translatable="false">Conversations</string> @@ -215,7 +215,7 @@ <string name="this_device_omemo_fingerprint">Own OMEMO fingerprint</string> <string name="other_devices">Other devices</string> <string name="trust_omemo_fingerprints">Trust OMEMO Fingerprints</string> - <string name="fetching_keys">Fetching keys...</string> + <string name="fetching_keys">Fetching keys…</string> <string name="done">Done</string> <string name="verify">Verify</string> <string name="decrypt">Decrypt</string> @@ -284,7 +284,7 @@ <string name="pref_about_conversations_summary">Build and licensing information</string> <string name="pref_about_message" translatable="false"> Conversations • the very last word in instant messaging. - \n\nCopyright © 2014-2015 Daniel Gultsch + \n\nCopyright © 2014 Daniel Gultsch \n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -475,7 +475,8 @@ <string name="offering_x_file">Offering %s</string> <string name="hide_offline">Hide offline</string> <string name="disable_account">Disable Account</string> - <string name="contact_is_typing">%s is typing...</string> + <string name="contact_is_typing">%s is typing…</string> + <string name="is_typing">is typing…</string> <string name="contact_has_stopped_typing">%s has stopped typing</string> <string name="pref_chat_states">Typing notifications</string> <string name="pref_chat_states_summary">Let your contact know when you are writing a new message</string> @@ -516,15 +517,27 @@ <string name="download_failed_server_not_found">Download failed: Server not found</string> <string name="download_failed_file_not_found">Download failed: File not found</string> <string name="download_failed_could_not_connect">Download failed: Could not connect to host</string> + <string name="elv_undo">undo</string> <string name="pref_use_white_background">Use white background</string> <string name="pref_use_white_background_summary">Show received messages as black text on a white background</string> <string name="account_status_dns_timeout">Timeout in DNS</string> + <string name="action_check_update">Check for Updates</string> + <string name="title_activity_updater">Update Service</string> + <string name="update_available">Conversations %1$s with the following changes is available:\n\n%2$s\n\nUpdate Conversations %3$s to Conversations %1$s now?</string> + <string name="checking_for_updates">Checking for Conversations updates</string> + <string name="remind_later">later</string> + <string name="update">Update</string> + <string name="new_version">New version:</string> + <string name="current_version">installed version:</string> + <string name="no_update_available">No update available</string> + <string name="download_started">Download started</string> <string name="server_info_broken">Broken</string> <string name="pref_presence_settings">Presence settings</string> <string name="pref_away_when_screen_off">Away when screen is off</string> <string name="pref_away_when_screen_off_summary">Marks your resource as away when the screen is turned off</string> <string name="pref_xa_on_silent_mode">Not available in silent mode</string> <string name="pref_xa_on_silent_mode_summary">Marks your resource as not available when phone is in silent mode</string> + <string name="update_info">Conversation is checking for an update. If an update is available you will be asked, if you want to update your version. The update process is downloading and installing the new version automatically.</string> <string name="action_add_account_with_certificate">Add account with certificate</string> <string name="unable_to_parse_certificate">Unable to parse certificate</string> <string name="authenticate_with_certificate">Leave empty to authenticate w/ certificate</string> diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml index f05738ac5..183e37233 100644 --- a/src/main/res/xml/preferences.xml +++ b/src/main/res/xml/preferences.xml @@ -16,7 +16,7 @@ android:summary="@string/pref_xmpp_resource_summary" android:title="@string/pref_xmpp_resource"/> <ListPreference - android:defaultValue="524288" + android:defaultValue="1048576" android:entries="@array/filesizes" android:entryValues="@array/filesizes_values" android:key="auto_accept_file_size" @@ -30,7 +30,7 @@ android:title="@string/pref_confirm_messages"/> <CheckBoxPreference - android:defaultValue="false" + android:defaultValue="true" android:key="chat_states" android:summary="@string/pref_chat_states_summary" android:title="@string/pref_chat_states"/> @@ -106,7 +106,7 @@ android:summary="@string/pref_use_larger_font_summary" android:title="@string/pref_use_larger_font"/> <CheckBoxPreference - android:defaultValue="false" + android:defaultValue="true" android:key="send_button_status" android:summary="@string/pref_use_send_button_to_indicate_status_summary" android:title="@string/pref_use_send_button_to_indicate_status"/> @@ -123,6 +123,11 @@ android:key="show_dynamic_tags" android:summary="@string/pref_show_dynamic_tags_summary" android:title="@string/pref_show_dynamic_tags"/> + <CheckBoxPreference + android:defaultValue="true" + android:key="indicate_received" + android:summary="@string/pref_use_indicate_received_summary" + android:title="@string/pref_use_indicate_received"/> </PreferenceCategory> <PreferenceCategory android:key="advanced" @@ -173,12 +178,7 @@ </PreferenceCategory> <PreferenceCategory android:title="@string/pref_expert_options_other"> <CheckBoxPreference - android:defaultValue="false" - android:key="indicate_received" - android:summary="@string/pref_use_indicate_received_summary" - android:title="@string/pref_use_indicate_received"/> - <CheckBoxPreference - android:defaultValue="false" + android:defaultValue="true" android:key="keep_foreground_service" android:summary="@string/pref_keep_foreground_service_summary" android:title="@string/pref_keep_foreground_service"/> @@ -190,7 +190,7 @@ </PreferenceScreen> <CheckBoxPreference - android:defaultValue="false" + android:defaultValue="true" android:key="never_send" android:summary="@string/pref_never_send_crash_summary" android:title="@string/pref_never_send_crash"/> |