diff --git a/briar-android/build.gradle b/briar-android/build.gradle index 469df63b22a344d580c5f68e37b1d19cbdf57fb5..11ddb77b20a2f7fa5797258ef242a1b80b8fb308 100644 --- a/briar-android/build.gradle +++ b/briar-android/build.gradle @@ -25,6 +25,7 @@ dependencies { exclude module: 'recyclerview-v7' } compile "com.android.support:cardview-v7:$supportVersion" + compile 'com.android.support:support-annotations:23.4.0' compile('ch.acra:acra:4.8.5') { exclude module: 'support-v4' exclude module: 'support-annotations' @@ -37,6 +38,7 @@ dependencies { provided 'javax.annotation:jsr250-api:1.0' compile 'com.jpardogo.materialtabstrip:library:1.1.0' compile 'com.github.bumptech.glide:glide:3.7.0' + compile 'uk.co.samuelwall:material-tap-target-prompt:1.3.0' testCompile 'junit:junit:4.12' testCompile 'net.jodah:concurrentunit:0.4.2' @@ -57,13 +59,14 @@ dependencyVerification { 'com.android.support:appcompat-v7:00f9d93acacd6731f309724054bf51492814b4b2869f16d7d5c0038dcb8c9a0d', 'com.android.support:preference-v14:44881bb46094e86d0bc2426f205419674a5b4eb514b44b5a4659b5de29f71eb7', 'com.android.support:design:003e0c0bea0a6891f8b2bc43f20ae7af2a49a17363e5bb10df5ee0bae12fa686', - 'com.android.support:support-annotations:786ab0d060774fb95cfdaf4878771e14b85733b1af9d72a4aae762dc7c1dff9f', + 'com.android.support:support-annotations:e91a88dd0c5e99069b7f09d4a46b5e06f1e9c4c72fc0a8e987e25d86af480f01', 'com.android.support:animated-vector-drawable:06d1963b85aa917099d7757e6a7b3e4dc06889413dc747f625ae8683606db3a1', 'com.android.support:support-vector-drawable:799bafe4c3de812386f0b291f744d5d6876452722dd40189b9ab87dbbf594ea1', 'com.android.support:recyclerview-v7:44040a888e23e0c93162a3377cfe06751080e3c22d369ab0d4301ef60d63b0fe', 'com.android.support:cardview-v7:4595f1c4a28cfa083b6c0920ad4d49e1c2ca4b8302a955e548f68eb63b74931b', 'com.jpardogo.materialtabstrip:library:24d19232b319f8c73e25793432357919a7ed972186f57a3b2c9093ea74ad8311', 'com.github.bumptech.glide:glide:76ef123957b5fbaebb05fcbe6606dd58c3bc3fcdadb257f99811d0ac9ea9b88b', + 'uk.co.samuelwall:material-tap-target-prompt:f67e1caead12a914525b32cbf6da52a96b93ff89573f93cb41102ef3130fb64a', ] } @@ -122,10 +125,6 @@ android { warning 'MissingTranslation' warning 'ImpliedQuantity' } - - dexOptions { - incremental true - } } task downloadTorGeoIp(type: Download) { diff --git a/briar-android/res/drawable/ic_more_vert_accent.xml b/briar-android/res/drawable/ic_more_vert_accent.xml new file mode 100644 index 0000000000000000000000000000000000000000..13b8c957f9eb1a70c9d2c1b1dba7bc4c13e99e31 --- /dev/null +++ b/briar-android/res/drawable/ic_more_vert_accent.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24.0" + android:viewportWidth="24.0"> + <path + android:fillColor="#FF2D3E50" + android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/> +</vector> diff --git a/briar-android/res/menu/conversation_actions.xml b/briar-android/res/menu/conversation_actions.xml index a8c95182ee9e50cab740c6be82d90d74fd39619c..c2beb5c2a2b4611767ff9756e79d43bb9f0ab003 100644 --- a/briar-android/res/menu/conversation_actions.xml +++ b/briar-android/res/menu/conversation_actions.xml @@ -1,13 +1,16 @@ <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto"> + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> <item android:id="@+id/action_introduction" android:icon="@drawable/introduction_white" android:title="@string/introduction_button" - app:showAsAction="never"/> + android:visible="false" + app:showAsAction="never" + tools:visible="true"/> <item android:id="@+id/action_social_remove_person" diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml index 626f3d8a8871164dc589d41bf566a512ad1edfec..c0a570c89c0c79312c502fdf36c4e7df5bd1ab30 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -114,6 +114,8 @@ <string name="connection_aborted_remote">Connection aborted by your contact! This could mean that someone is trying to interfere with your connection</string> <!-- Introductions --> + <string name="introduction_onboarding_title">Introduce your contacts</string> + <string name="introduction_onboarding_text">You can introduce your contacts to each other, so they don\'t need to meet in person to connect on Briar.</string> <string name="introduction_activity_title">Select Contact</string> <string name="introduction_message_title">Introduce Contacts</string> <string name="introduction_message_text">You can compose a message that will be sent to %1$s and %2$s along with your introduction:</string> diff --git a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java index 3055777301450e5826bd96c2326bcb208f23d374..da81a4afac23fc53107762731203402c9ac1f726 100644 --- a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java +++ b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java @@ -10,12 +10,15 @@ import android.support.v4.content.ContextCompat; import android.support.v4.view.ViewCompat; import android.support.v7.app.ActionBar; import android.support.v7.app.AlertDialog; +import android.support.v7.widget.ActionMenuView; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.Toolbar; import android.util.SparseArray; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; @@ -62,6 +65,8 @@ import org.briarproject.api.messaging.PrivateMessage; import org.briarproject.api.messaging.PrivateMessageFactory; import org.briarproject.api.messaging.PrivateMessageHeader; import org.briarproject.api.plugins.ConnectionRegistry; +import org.briarproject.api.settings.Settings; +import org.briarproject.api.settings.SettingsManager; import org.briarproject.api.sharing.InvitationMessage; import org.briarproject.api.sharing.InvitationRequest; import org.briarproject.api.sharing.InvitationResponse; @@ -84,6 +89,8 @@ import javax.inject.Inject; import de.hdodenhof.circleimageview.CircleImageView; import im.delight.android.identicons.IdenticonDrawable; +import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt; +import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.OnHidePromptListener; import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation; import static android.widget.Toast.LENGTH_SHORT; @@ -91,6 +98,7 @@ import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static org.briarproject.android.contact.ConversationItem.IncomingItem; import static org.briarproject.android.contact.ConversationItem.OutgoingItem; +import static org.briarproject.android.fragment.SettingsFragment.SETTINGS_NAMESPACE; public class ConversationActivity extends BriarActivity implements EventListener, IntroductionHandler, TextInputListener { @@ -98,6 +106,8 @@ public class ConversationActivity extends BriarActivity private static final Logger LOG = Logger.getLogger(ConversationActivity.class.getName()); private static final int REQUEST_CODE_INTRODUCTION = 1; + public static final String SHOW_ONBOARDING_INTRODUCTION = + "showOnboardingIntroduction"; @Inject AndroidNotificationManager notificationManager; @@ -108,6 +118,7 @@ public class ConversationActivity extends BriarActivity protected Executor cryptoExecutor; private ConversationAdapter adapter; + private Toolbar toolbar; private CircleImageView toolbarAvatar; private ImageView toolbarStatus; private TextView toolbarTitle; @@ -122,6 +133,8 @@ public class ConversationActivity extends BriarActivity @Inject protected volatile EventBus eventBus; @Inject + protected volatile SettingsManager settingsManager; + @Inject volatile PrivateMessageFactory privateMessageFactory; @Inject protected volatile IntroductionManager introductionManager; @@ -150,13 +163,13 @@ public class ConversationActivity extends BriarActivity setContentView(R.layout.activity_conversation); // Custom Toolbar - Toolbar tb = (Toolbar) findViewById(R.id.toolbar); - if (tb != null) { + toolbar = (Toolbar) findViewById(R.id.toolbar); + if (toolbar != null) { toolbarAvatar = - (CircleImageView) tb.findViewById(R.id.contactAvatar); - toolbarStatus = (ImageView) tb.findViewById(R.id.contactStatus); - toolbarTitle = (TextView) tb.findViewById(R.id.contactName); - setSupportActionBar(tb); + (CircleImageView) toolbar.findViewById(R.id.contactAvatar); + toolbarStatus = (ImageView) toolbar.findViewById(R.id.contactStatus); + toolbarTitle = (TextView) toolbar.findViewById(R.id.contactName); + setSupportActionBar(toolbar); } ActionBar ab = getSupportActionBar(); if (ab != null) { @@ -222,7 +235,7 @@ public class ConversationActivity extends BriarActivity MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.conversation_actions, menu); - hideIntroductionActionWhenOneContact( + showIntroductionActionIfAvailable( menu.findItem(R.id.action_introduction)); return super.onCreateOptionsMenu(menu); @@ -742,13 +755,19 @@ public class ConversationActivity extends BriarActivity }); } - private void hideIntroductionActionWhenOneContact(final MenuItem item) { + private void showIntroductionActionIfAvailable(final MenuItem item) { runOnDbThread(new Runnable() { @Override public void run() { try { - if (contactManager.getActiveContacts().size() < 2) { - hideIntroductionAction(item); + if (contactManager.getActiveContacts().size() > 1) { + showIntroductionAction(item); + Settings settings = + settingsManager.getSettings(SETTINGS_NAMESPACE); + if (settings.getBoolean(SHOW_ONBOARDING_INTRODUCTION, + true)) { + showIntroductionOnboarding(); + } } } catch (DbException e) { if (LOG.isLoggable(WARNING)) @@ -758,11 +777,69 @@ public class ConversationActivity extends BriarActivity }); } - private void hideIntroductionAction(final MenuItem item) { + private void showIntroductionAction(final MenuItem item) { + runOnUiThread(new Runnable() { + @Override + public void run() { + item.setVisible(true); + } + }); + } + + private void showIntroductionOnboarding() { runOnUiThread(new Runnable() { @Override public void run() { - item.setVisible(false); + // find view of overflow icon + View target = null; + for (int i = 0; i < toolbar.getChildCount(); i++) { + if (toolbar.getChildAt(i) instanceof ActionMenuView) { + ActionMenuView menu = + (ActionMenuView) toolbar.getChildAt(i); + target = menu.getChildAt(menu.getChildCount() - 1); + break; + } + } + if (target == null) { + LOG.warning("No Overflow Icon found!"); + return; + } + + OnHidePromptListener listener = new OnHidePromptListener() { + @Override + public void onHidePrompt(MotionEvent motionEvent, + boolean focalClicked) { + if (focalClicked) introductionOnboardingSeen(); + } + + @Override + public void onHidePromptComplete() { + } + }; + new MaterialTapTargetPrompt.Builder(ConversationActivity.this) + .setTarget(target) + .setPrimaryText(R.string.introduction_onboarding_title) + .setSecondaryText(R.string.introduction_onboarding_text) + .setBackgroundColourFromRes(R.color.briar_primary) + .setIcon(R.drawable.ic_more_vert_accent) + .setOnHidePromptListener(listener) + .show(); + } + }); + } + + private void introductionOnboardingSeen() { + runOnDbThread(new Runnable() { + @Override + public void run() { + try { + Settings settings = new Settings(); + settings.putBoolean(SHOW_ONBOARDING_INTRODUCTION, false); + settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE); + } catch (DbException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + } } }); }