diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index df4c11fd2..4aeca16b2 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -72,6 +72,7 @@
+
@@ -213,6 +214,7 @@
android:windowSoftInputMode="stateUnchanged"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
+ android:exported="true"
android:parentActivityName=".ConversationListActivity">
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5dcad7e4a..f9f8c7bca 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -135,6 +135,7 @@
Group members
Invalid recipient!
+ Added to home screen
Calls not supported
This device does not appear to support dial actions.
Leave group?
@@ -313,7 +314,7 @@
GIFs
Stickers
-
+
New group
Edit group
@@ -819,7 +820,7 @@
Off
-
+
- %d second
- %d seconds
@@ -854,7 +855,7 @@
%dw
-
+
Your safety number with %s has changed and is no longer verified
Your safety numbers with %1$s and %2$s are no longer verified
@@ -1161,7 +1162,7 @@
Message font size
Contact joined Signal
Priority
-
+
@@ -1389,6 +1390,7 @@
Signal is locked
TAP TO UNLOCK
Reminder:
+ Add to home screen
About
diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java
index a064b45ef..771f989b2 100644
--- a/src/org/thoughtcrime/securesms/ConversationActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationActivity.java
@@ -568,6 +568,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
case R.id.menu_call_secure:
case R.id.menu_call_insecure: handleDial(getRecipient()); return true;
case R.id.menu_view_media: handleViewMedia(); return true;
+ case R.id.menu_add_shortcut: handleAddShortcut(); return true;
case R.id.menu_add_to_contacts: handleAddToContacts(); return true;
case R.id.menu_reset_secure_session: handleResetSecureSession(); return true;
case R.id.menu_group_recipients: handleDisplayGroupRecipients(); return true;
@@ -777,6 +778,37 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
startActivity(intent);
}
+ private void handleAddShortcut() {
+ Log.i(TAG, "Creating home screen shortcut for recipient " + recipient.getAddress());
+
+ Intent launchIntent = new Intent(getApplicationContext(), ConversationActivity.class);
+
+ launchIntent.putExtra(ADDRESS_EXTRA, recipient.getAddress().serialize());
+ launchIntent.putExtra(TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.TEXT_EXTRA));
+ launchIntent.setDataAndType(getIntent().getData(), getIntent().getType());
+
+ long existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient);
+
+ launchIntent.putExtra(ConversationActivity.THREAD_ID_EXTRA, existingThread);
+ launchIntent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
+
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+ Intent intent = new Intent();
+ final String name = Optional.fromNullable(recipient.getProfileName())
+ .or(Optional.fromNullable(recipient.getName()))
+ .or(recipient.toShortString());
+ // these constants are deprecated but their replacement (ShortcutManager) is available only from API level 25
+ intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, launchIntent);
+ intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
+ intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(getApplicationContext(), R.mipmap.ic_launcher));
+ intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
+
+ getApplicationContext().sendBroadcast(intent);
+ Toast.makeText(this, getString(R.string.ConversationActivity_added_to_home_screen), Toast.LENGTH_LONG).show();
+ }
+
private void handleLeavePushGroup() {
if (getRecipient() == null) {
Toast.makeText(this, getString(R.string.ConversationActivity_invalid_recipient),
@@ -1348,8 +1380,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void initializeResources() {
if (recipient != null) recipient.removeListener(this);
-
- recipient = Recipient.from(this, getIntent().getParcelableExtra(ADDRESS_EXTRA), true);
+ recipient = getRecipientFromExtras(getIntent(), this);
threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1);
archived = getIntent().getBooleanExtra(IS_ARCHIVED_EXTRA, false);
distributionType = getIntent().getIntExtra(DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
@@ -1364,6 +1395,26 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
recipient.addListener(this);
}
+ /**
+ * Extracts the Recipient instance from the extras contained in the intent.
+ *
+ * This can be passed in two ways:
+ *
+ * - If the intent was started from inside the app, the address is a parcelable Address instance.
+ * - If it was launched from the home screen then it is a serialised (stringified) form of the Address, as home screen
+ * shortcuts cannot contain instances of Address (see BadParcelableException).
+ */
+ static Recipient getRecipientFromExtras(@NonNull Intent intent, @NonNull Context context) {
+ Address address;
+ final Address parcelableAddress = intent.getParcelableExtra(ADDRESS_EXTRA);
+ if(parcelableAddress != null) {
+ address = parcelableAddress;
+ } else {
+ address = Address.fromSerialized((String) intent.getExtras().get(ADDRESS_EXTRA));
+ }
+ return Recipient.from(context, address, true);
+ }
+
private void initializeProfiles() {
if (!isSecureText) {
Log.i(TAG, "SMS contact, no profile fetch needed.");
diff --git a/src/org/thoughtcrime/securesms/ConversationFragment.java b/src/org/thoughtcrime/securesms/ConversationFragment.java
index 9ec757fc3..5f256cd2a 100644
--- a/src/org/thoughtcrime/securesms/ConversationFragment.java
+++ b/src/org/thoughtcrime/securesms/ConversationFragment.java
@@ -206,7 +206,7 @@ public class ConversationFragment extends Fragment
}
private void initializeResources() {
- this.recipient = Recipient.from(getActivity(), getActivity().getIntent().getParcelableExtra(ConversationActivity.ADDRESS_EXTRA), true);
+ this.recipient = ConversationActivity.getRecipientFromExtras(getActivity().getIntent(), getActivity());
this.threadId = this.getActivity().getIntent().getLongExtra(ConversationActivity.THREAD_ID_EXTRA, -1);
this.lastSeen = this.getActivity().getIntent().getLongExtra(ConversationActivity.LAST_SEEN_EXTRA, -1);
this.startingPosition = this.getActivity().getIntent().getIntExtra(ConversationActivity.STARTING_POSITION_EXTRA, -1);