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);