From fbe5df8938ce77e1bc0c5c04b4cca84505374c14 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Fri, 16 Nov 2018 18:58:16 -0200 Subject: [PATCH] [android] Add ImageActivity to show images in full-screen --- briar-android/build.gradle | 1 + briar-android/src/main/AndroidManifest.xml | 9 + .../android/activity/ActivityComponent.java | 5 +- .../briar/android/activity/BriarActivity.java | 31 +-- .../briar/android/blog/ReblogActivity.java | 1 - .../android/conversation/AttachmentItem.java | 48 ++++- .../conversation/ConversationActivity.java | 41 +++- .../conversation/ConversationListener.java | 4 + .../ConversationMessageViewHolder.java | 12 +- .../android/conversation/ImageActivity.java | 181 ++++++++++++++++++ .../briar/android/util/UiUtils.java | 19 ++ .../src/main/res/layout/activity_image.xml | 61 ++++++ briar-android/src/main/res/values/strings.xml | 2 + briar-android/witness.gradle | 1 + build.gradle | 1 + 15 files changed, 397 insertions(+), 20 deletions(-) create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageActivity.java create mode 100644 briar-android/src/main/res/layout/activity_image.xml diff --git a/briar-android/build.gradle b/briar-android/build.gradle index 87748420d..c52aced57 100644 --- a/briar-android/build.gradle +++ b/briar-android/build.gradle @@ -123,6 +123,7 @@ dependencies { exclude group: 'com.android.support' exclude module: 'disklrucache' // when there's no disk cache, we can't accidentally use it } + implementation 'com.github.chrisbanes:PhotoView:2.1.4' // later versions already use androidx annotationProcessor 'com.google.dagger:dagger-compiler:2.19' annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion" diff --git a/briar-android/src/main/AndroidManifest.xml b/briar-android/src/main/AndroidManifest.xml index e9300a7fd..c0e358843 100644 --- a/briar-android/src/main/AndroidManifest.xml +++ b/briar-android/src/main/AndroidManifest.xml @@ -113,6 +113,15 @@ /> + + + + new activity. + * @param returnTransition used when window is closing, because the activity is finishing. + */ + @RequiresApi(api = 21) + public void setSceneTransitionAnimation( + @Nullable Transition enterTransition, + @Nullable Transition exitTransition, + @Nullable Transition returnTransition) { // workaround for #1007 if (isSamsung7()) { return; } - Transition slide = new Slide(Gravity.RIGHT); - slide.excludeTarget(android.R.id.statusBarBackground, true); - slide.excludeTarget(android.R.id.navigationBarBackground, true); + if (enterTransition != null) excludeSystemUi(enterTransition); + if (exitTransition != null) excludeSystemUi(exitTransition); + if (returnTransition != null) excludeSystemUi(returnTransition); Window window = getWindow(); - window.requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); - window.setExitTransition(slide); - window.setEnterTransition(slide); - window.setTransitionBackgroundFadeDuration(getResources() - .getInteger(android.R.integer.config_longAnimTime)); + window.setEnterTransition(enterTransition); + window.setExitTransition(exitTransition); + window.setReturnTransition(returnTransition); } /** diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/ReblogActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/ReblogActivity.java index 7877cbc3b..d45b10789 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/ReblogActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/ReblogActivity.java @@ -18,7 +18,6 @@ public class ReblogActivity extends BriarActivity implements @Override public void onCreate(Bundle savedInstanceState) { - setSceneTransitionAnimation(); super.onCreate(savedInstanceState); Intent intent = getIntent(); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/AttachmentItem.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/AttachmentItem.java index 5a5efb657..bd582d68f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/AttachmentItem.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/AttachmentItem.java @@ -1,5 +1,8 @@ package org.briarproject.briar.android.conversation; +import android.os.Parcel; +import android.os.Parcelable; + import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.sync.MessageId; @@ -7,13 +10,26 @@ import javax.annotation.concurrent.Immutable; @Immutable @NotNullByDefault -public class AttachmentItem { +public class AttachmentItem implements Parcelable { private final MessageId messageId; private final int width, height; private final int thumbnailWidth, thumbnailHeight; private final boolean hasError; + public static final Creator CREATOR = + new Creator() { + @Override + public AttachmentItem createFromParcel(Parcel in) { + return new AttachmentItem(in); + } + + @Override + public AttachmentItem[] newArray(int size) { + return new AttachmentItem[size]; + } + }; + AttachmentItem(MessageId messageId, int width, int height, int thumbnailWidth, int thumbnailHeight, boolean hasError) { this.messageId = messageId; @@ -24,6 +40,17 @@ public class AttachmentItem { this.hasError = hasError; } + protected AttachmentItem(Parcel in) { + byte[] messageIdByte = new byte[MessageId.LENGTH]; + in.readByteArray(messageIdByte); + messageId = new MessageId(messageIdByte); + width = in.readInt(); + height = in.readInt(); + thumbnailWidth = in.readInt(); + thumbnailHeight = in.readInt(); + hasError = in.readByte() != 0; + } + public MessageId getMessageId() { return messageId; } @@ -48,4 +75,23 @@ public class AttachmentItem { return hasError; } + String getTransitionName() { + return String.valueOf(messageId.hashCode()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeByteArray(messageId.getBytes()); + dest.writeInt(width); + dest.writeInt(height); + dest.writeInt(thumbnailWidth); + dest.writeInt(thumbnailHeight); + dest.writeByte((byte) (hasError ? 1 : 0)); + } + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java index 754e8f06a..e17ad5451 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java @@ -9,11 +9,15 @@ import android.os.Bundle; import android.support.annotation.Nullable; import android.support.annotation.UiThread; import android.support.design.widget.Snackbar; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.content.ContextCompat; 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.transition.Slide; +import android.transition.Transition; import android.util.SparseArray; import android.view.Menu; import android.view.MenuInflater; @@ -96,8 +100,11 @@ import im.delight.android.identicons.IdenticonDrawable; import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt; import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.PromptStateChangeListener; +import static android.os.Build.VERSION.SDK_INT; +import static android.support.v4.app.ActivityOptionsCompat.makeSceneTransitionAnimation; import static android.support.v4.view.ViewCompat.setTransitionName; import static android.support.v7.util.SortedList.INVALID_POSITION; +import static android.view.Gravity.END; import static android.widget.Toast.LENGTH_SHORT; import static java.util.Collections.emptyList; import static java.util.Collections.sort; @@ -108,6 +115,9 @@ import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_INTRODUCTION; +import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHMENT; +import static org.briarproject.briar.android.conversation.ImageActivity.DATE; +import static org.briarproject.briar.android.conversation.ImageActivity.NAME; import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE; import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName; import static org.briarproject.briar.android.util.UiUtils.getBulbTransitionName; @@ -186,7 +196,10 @@ public class ConversationActivity extends BriarActivity @Override public void onCreate(@Nullable Bundle state) { - setSceneTransitionAnimation(); + if (SDK_INT >= 21) { + Transition slide = new Slide(END); + setSceneTransitionAnimation(slide, null, slide); + } super.onCreate(state); Intent i = getIntent(); @@ -802,6 +815,31 @@ public class ConversationActivity extends BriarActivity startActivity(i); } + @Override + public void onAttachmentClicked(View view, + ConversationMessageItem messageItem, AttachmentItem item) { + String name; + if (messageItem.isIncoming()) { + // must be available when items are being displayed + name = viewModel.getContactDisplayName().getValue(); + } else { + name = getString(R.string.you); + } + Intent i = new Intent(this, ImageActivity.class); + i.putExtra(ATTACHMENT, item); + i.putExtra(NAME, name); + i.putExtra(DATE, messageItem.getTime()); + if (SDK_INT >= 23) { + String transitionName = item.getTransitionName(); + ActivityOptionsCompat options = + makeSceneTransitionAnimation(this, view, transitionName); + ActivityCompat.startActivity(this, i, options.toBundle()); + } else { + // work-around for android bug #224270 + startActivity(i); + } + } + @DatabaseExecutor private void respondToIntroductionRequest(SessionId sessionId, boolean accept, long time) throws DbException { @@ -845,4 +883,5 @@ public class ConversationActivity extends BriarActivity } return attachments; } + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationListener.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationListener.java index 0b56878dc..9c8db9538 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationListener.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationListener.java @@ -1,6 +1,7 @@ package org.briarproject.briar.android.conversation; import android.support.annotation.UiThread; +import android.view.View; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; @@ -14,4 +15,7 @@ interface ConversationListener { void openRequestedShareable(ConversationRequestItem item); + void onAttachmentClicked(View view, ConversationMessageItem messageItem, + AttachmentItem attachmentItem); + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationMessageViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationMessageViewHolder.java index 91977da2c..abef0bffd 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationMessageViewHolder.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationMessageViewHolder.java @@ -85,7 +85,7 @@ class ConversationMessageViewHolder extends ConversationItemViewHolder { if (item.getAttachments().isEmpty()) { bindTextItem(); } else { - bindImageItem(item); + bindImageItem(item, listener); } } @@ -98,7 +98,8 @@ class ConversationMessageViewHolder extends ConversationItemViewHolder { textConstraints.applyTo(layout); } - private void bindImageItem(ConversationMessageItem item) { + private void bindImageItem(ConversationMessageItem item, + ConversationListener listener) { // TODO show more than just the first image AttachmentItem attachment = item.getAttachments().get(0); @@ -127,17 +128,18 @@ class ConversationMessageViewHolder extends ConversationItemViewHolder { clearImage(); imageView.setImageResource(ERROR_RES); } else { - loadImage(item, attachment); + loadImage(item, attachment, listener); } } private void clearImage() { GlideApp.with(imageView) .clear(imageView); + imageView.setOnClickListener(null); } private void loadImage(ConversationMessageItem item, - AttachmentItem attachment) { + AttachmentItem attachment, ConversationListener listener) { boolean leftCornerSmall = (isIncoming() && !isRtl) || (!isIncoming() && isRtl); boolean bottomRound = item.getText() == null; @@ -152,6 +154,8 @@ class ConversationMessageViewHolder extends ConversationItemViewHolder { .transition(withCrossFade()) .into(imageView) .waitForLayout(); + imageView.setOnClickListener( + view -> listener.onAttachmentClicked(view, item, attachment)); } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageActivity.java new file mode 100644 index 000000000..6c489d710 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageActivity.java @@ -0,0 +1,181 @@ +package org.briarproject.briar.android.conversation; + +import android.graphics.drawable.Animatable; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; +import android.support.design.widget.AppBarLayout; +import android.support.v7.widget.Toolbar; +import android.transition.Fade; +import android.transition.Transition; +import android.view.MenuItem; +import android.view.View; +import android.view.Window; +import android.widget.TextView; + +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.engine.GlideException; +import com.bumptech.glide.request.RequestListener; +import com.bumptech.glide.request.target.Target; +import com.github.chrisbanes.photoview.PhotoView; + +import org.briarproject.briar.R; +import org.briarproject.briar.android.activity.ActivityComponent; +import org.briarproject.briar.android.activity.BriarActivity; +import org.briarproject.briar.android.conversation.glide.GlideApp; + +import static android.graphics.Color.TRANSPARENT; +import static android.os.Build.VERSION.SDK_INT; +import static android.view.View.GONE; +import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN; +import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; +import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE; +import static android.view.View.VISIBLE; +import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; +import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE; +import static java.util.Objects.requireNonNull; +import static org.briarproject.briar.android.util.UiUtils.formatDateAbsolute; + +public class ImageActivity extends BriarActivity { + + final static String ATTACHMENT = "attachment"; + final static String NAME = "name"; + final static String DATE = "date"; + + private AppBarLayout appBarLayout; + + @Override + public void injectActivity(ActivityComponent component) { + component.inject(this); + } + + @Override + public void onCreate(@Nullable Bundle state) { + super.onCreate(state); + + // Transitions + supportPostponeEnterTransition(); + Window window = getWindow(); + if (SDK_INT >= 21) { + Transition transition = new Fade(); + setSceneTransitionAnimation(transition, null, transition); + } + + // inflate layout + setContentView(R.layout.activity_image); + + // Status Bar + if (SDK_INT >= 21) { + window.setStatusBarColor(TRANSPARENT); + } else if (SDK_INT >= 19) { + // we can't make the status bar transparent, but translucent + window.addFlags(FLAG_TRANSLUCENT_STATUS); + } + + // Toolbar + appBarLayout = findViewById(R.id.appBarLayout); + Toolbar toolbar = requireNonNull(setUpCustomToolbar(true)); + TextView contactName = toolbar.findViewById(R.id.contactName); + TextView dateView = toolbar.findViewById(R.id.dateView); + + // Intent Extras + AttachmentItem attachment = getIntent().getParcelableExtra(ATTACHMENT); + String name = getIntent().getStringExtra(NAME); + long time = getIntent().getLongExtra(DATE, 0); + String date = formatDateAbsolute(this, time); + contactName.setText(name); + dateView.setText(date); + + // Image View + PhotoView photoView = findViewById(R.id.photoView); + if (SDK_INT >= 16) { + photoView.setOnClickListener(view -> toggleSystemUi()); + window.getDecorView().setSystemUiVisibility( + SYSTEM_UI_FLAG_LAYOUT_STABLE | + SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); + } + + // Request Listener + RequestListener listener = new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, + Object model, Target target, + boolean isFirstResource) { + return false; + } + + @Override + public boolean onResourceReady(Drawable resource, + Object model, Target target, + DataSource dataSource, boolean isFirstResource) { + if (SDK_INT >= 21 && + !(resource instanceof Animatable)) { + // set transition name only when not animatable, + // because the animation won't start otherwise + photoView.setTransitionName( + attachment.getTransitionName()); + } + supportStartPostponedEnterTransition(); + return false; + } + }; + + // Load Image + GlideApp.with(this) + .load(attachment) + .diskCacheStrategy(NONE) + .error(R.drawable.ic_image_broken) + .dontTransform() + .addListener(listener) + .into(photoView); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + onBackPressed(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @RequiresApi(api = 16) + private void toggleSystemUi() { + View decorView = getWindow().getDecorView(); + if (appBarLayout.getVisibility() == VISIBLE) { + hideSystemUI(decorView); + } else { + showSystemUI(decorView); + } + } + + @RequiresApi(api = 16) + private void hideSystemUI(View decorView) { + decorView.setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_STABLE + | SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | SYSTEM_UI_FLAG_FULLSCREEN + ); + appBarLayout.animate() + .translationYBy(-1 * appBarLayout.getHeight()) + .alpha(0f) + .withEndAction(() -> appBarLayout.setVisibility(GONE)) + .start(); + } + + @RequiresApi(api = 16) + private void showSystemUI(View decorView) { + decorView.setSystemUiVisibility( + SYSTEM_UI_FLAG_LAYOUT_STABLE + | SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + ); + appBarLayout.animate() + .translationYBy(appBarLayout.getHeight()) + .alpha(1f) + .withStartAction(() -> appBarLayout.setVisibility(VISIBLE)) + .start(); + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java index 5f59251f9..c73214fb9 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java @@ -16,6 +16,7 @@ import android.support.annotation.ColorInt; import android.support.annotation.ColorRes; import android.support.annotation.MainThread; import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; import android.support.design.widget.TextInputLayout; import android.support.v4.app.FragmentManager; import android.support.v4.content.ContextCompat; @@ -31,6 +32,7 @@ import android.text.method.LinkMovementMethod; import android.text.style.ClickableSpan; import android.text.style.ForegroundColorSpan; import android.text.style.URLSpan; +import android.transition.Transition; import android.util.TypedValue; import android.view.KeyEvent; import android.view.View; @@ -60,12 +62,16 @@ import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_NO; import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_YES; import static android.support.v7.app.AppCompatDelegate.setDefaultNightMode; import static android.text.format.DateUtils.DAY_IN_MILLIS; +import static android.text.format.DateUtils.FORMAT_ABBREV_ALL; import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; import static android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE; import static android.text.format.DateUtils.FORMAT_ABBREV_TIME; import static android.text.format.DateUtils.FORMAT_SHOW_DATE; +import static android.text.format.DateUtils.FORMAT_SHOW_TIME; +import static android.text.format.DateUtils.FORMAT_SHOW_YEAR; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.DateUtils.WEEK_IN_MILLIS; +import static android.text.format.DateUtils.YEAR_IN_MILLIS; import static android.view.KeyEvent.ACTION_DOWN; import static android.view.KeyEvent.KEYCODE_ENTER; import static android.view.inputmethod.EditorInfo.IME_NULL; @@ -117,6 +123,13 @@ public class UiUtils { MIN_DATE_RESOLUTION, flags).toString(); } + public static String formatDateAbsolute(Context ctx, long time) { + int flags = FORMAT_SHOW_TIME | FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL; + long diff = System.currentTimeMillis() - time; + if (diff >= YEAR_IN_MILLIS) flags |= FORMAT_SHOW_YEAR; + return DateUtils.formatDateTime(ctx, time, flags); + } + public static int getDaysUntilExpiry() { long now = System.currentTimeMillis(); long daysBeforeExpiry = (EXPIRY_DATE - now) / 1000 / 60 / 60 / 24; @@ -318,6 +331,12 @@ public class UiUtils { keyEvent.getKeyCode() == KEYCODE_ENTER; } + @RequiresApi(api = 21) + public static void excludeSystemUi(Transition transition) { + transition.excludeTarget(android.R.id.statusBarBackground, true); + transition.excludeTarget(android.R.id.navigationBarBackground, true); + } + /** * Observes the given {@link LiveData} until the first change. * If the LiveData's value is available, the {@link Observer} will be diff --git a/briar-android/src/main/res/layout/activity_image.xml b/briar-android/src/main/res/layout/activity_image.xml new file mode 100644 index 000000000..2be562fbd --- /dev/null +++ b/briar-android/src/main/res/layout/activity_image.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index a7ee15079..2f87d2a26 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -134,6 +134,8 @@ Confirm Contact Deletion Are you sure that you want to remove this contact and all messages exchanged with this contact? Contact deleted + + You Add a Contact diff --git a/briar-android/witness.gradle b/briar-android/witness.gradle index 29a9c2f01..ee7273494 100644 --- a/briar-android/witness.gradle +++ b/briar-android/witness.gradle @@ -89,6 +89,7 @@ dependencyVerification { 'com.github.bumptech.glide:compiler:4.8.0:compiler-4.8.0.jar:1fa93dd0cf7ef0b8b98a59a67a1ee84915416c2d677d83a771ea3e32ad15e6bf', 'com.github.bumptech.glide:gifdecoder:4.8.0:gifdecoder-4.8.0.aar:b00c5454a023a9488ea49603930d9c25e09192e5ceaadf64977aa52946b3c1b4', 'com.github.bumptech.glide:glide:4.8.0:glide-4.8.0.aar:5ddf08b12cc43332e812988f16c2c39e7fce49d1c4d94b7948dcde7f00bf49d6', + 'com.github.chrisbanes:PhotoView:2.1.4:PhotoView-2.1.4.aar:04cb397fcb3df0757c8aed6927ebdd247930b5c78ee9acc59cd07dccdaaf3460', 'com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.0:accessibility-test-framework-2.0.jar:cdf16ef8f5b8023d003ce3cc1b0d51bda737762e2dab2fedf43d1c4292353f7f', 'com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.1:accessibility-test-framework-2.1.jar:7b0aa6ed7553597ce0610684a9f7eca8021eee218f2e2f427c04a7fbf5f920bd', 'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed', diff --git a/build.gradle b/build.gradle index 877e86667..516f8f7fe 100644 --- a/build.gradle +++ b/build.gradle @@ -5,6 +5,7 @@ allprojects { jcenter() mavenLocal() google() + maven { url "https://jitpack.io" } } afterEvaluate { tasks.withType(Test) {