(size);
+ for (int i = 0; i < size; i++) {
+ String key = parcel.readString();
+ String value = parcel.readString();
+ map.put(key, value);
+ }
+ }
+ return map;
+ }
+}
diff --git a/app/src/main/java/io/plaidapp/util/ScrimUtil.java b/app/src/main/java/io/plaidapp/util/ScrimUtil.java
new file mode 100644
index 000000000..359a19a89
--- /dev/null
+++ b/app/src/main/java/io/plaidapp/util/ScrimUtil.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.plaidapp.util;
+
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.PaintDrawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.RectShape;
+import android.support.annotation.ColorInt;
+import android.view.Gravity;
+
+/**
+ * * Borrowed from github.com/romannurik/muzei
+ */
+public class ScrimUtil {
+
+ private ScrimUtil() { }
+
+ /**
+ * Creates an approximated cubic gradient using a multi-stop linear gradient. See
+ * this post for more
+ * details.
+ */
+ public static Drawable makeCubicGradientScrimDrawable(@ColorInt int baseColor,
+ int numStops,
+ int gravity) {
+ numStops = Math.max(numStops, 2);
+
+ PaintDrawable paintDrawable = new PaintDrawable();
+ paintDrawable.setShape(new RectShape());
+
+ final int[] stopColors = new int[numStops];
+
+ int alpha = Color.alpha(baseColor);
+
+ for (int i = 0; i < numStops; i++) {
+ float x = i * 1f / (numStops - 1);
+ float opacity = MathUtils.constrain(0, 1, (float) Math.pow(x, 3));
+ stopColors[i] = ColorUtils.modifyAlpha(baseColor, (int) (alpha * opacity));
+ }
+
+ final float x0, x1, y0, y1;
+ switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.LEFT:
+ x0 = 1;
+ x1 = 0;
+ break;
+ case Gravity.RIGHT:
+ x0 = 0;
+ x1 = 1;
+ break;
+ default:
+ x0 = 0;
+ x1 = 0;
+ break;
+ }
+ switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
+ case Gravity.TOP:
+ y0 = 1;
+ y1 = 0;
+ break;
+ case Gravity.BOTTOM:
+ y0 = 0;
+ y1 = 1;
+ break;
+ default:
+ y0 = 0;
+ y1 = 0;
+ break;
+ }
+
+ paintDrawable.setShaderFactory(new ShapeDrawable.ShaderFactory() {
+ @Override
+ public Shader resize(int width, int height) {
+ LinearGradient linearGradient = new LinearGradient(
+ width * x0,
+ height * y0,
+ width * x1,
+ height * y1,
+ stopColors, null,
+ Shader.TileMode.CLAMP);
+ return linearGradient;
+ }
+ });
+
+ return paintDrawable;
+ }
+}
diff --git a/app/src/main/java/io/plaidapp/util/ViewOffsetHelper.java b/app/src/main/java/io/plaidapp/util/ViewOffsetHelper.java
new file mode 100644
index 000000000..c35fcfbe6
--- /dev/null
+++ b/app/src/main/java/io/plaidapp/util/ViewOffsetHelper.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.plaidapp.util;
+
+import android.os.Build;
+import android.support.v4.view.ViewCompat;
+import android.util.Property;
+import android.view.View;
+import android.view.ViewParent;
+
+/**
+ * Borrowed from the design lib.
+ *
+ * Utility helper for moving a {@link android.view.View} around using
+ * {@link android.view.View#offsetLeftAndRight(int)} and
+ * {@link android.view.View#offsetTopAndBottom(int)}.
+ *
+ * Also the setting of absolute offsets (similar to translationX/Y), rather than additive
+ * offsets.
+ */
+public class ViewOffsetHelper {
+
+ private final View mView;
+
+ private int mLayoutTop;
+ private int mLayoutLeft;
+ private int mOffsetTop;
+ private int mOffsetLeft;
+
+ public ViewOffsetHelper(View view) {
+ mView = view;
+ }
+
+ public void onViewLayout() {
+ // Now grab the intended top
+ mLayoutTop = mView.getTop();
+ mLayoutLeft = mView.getLeft();
+
+ // And offset it as needed
+ updateOffsets();
+ }
+
+ private void updateOffsets() {
+ ViewCompat.offsetTopAndBottom(mView, mOffsetTop - (mView.getTop() - mLayoutTop));
+ ViewCompat.offsetLeftAndRight(mView, mOffsetLeft - (mView.getLeft() - mLayoutLeft));
+
+ // Manually invalidate the view and parent to make sure we get drawn pre-M
+ if (Build.VERSION.SDK_INT < 23) {
+ tickleInvalidationFlag(mView);
+ final ViewParent vp = mView.getParent();
+ if (vp instanceof View) {
+ tickleInvalidationFlag((View) vp);
+ }
+ }
+ }
+
+ private static void tickleInvalidationFlag(View view) {
+ final float x = ViewCompat.getTranslationX(view);
+ ViewCompat.setTranslationY(view, x + 1);
+ ViewCompat.setTranslationY(view, x);
+ }
+
+ /**
+ * Set the top and bottom offset for this {@link ViewOffsetHelper}'s view.
+ *
+ * @param offset the offset in px.
+ * @return true if the offset has changed
+ */
+ public boolean setTopAndBottomOffset(int offset) {
+ if (mOffsetTop != offset) {
+ mOffsetTop = offset;
+ updateOffsets();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Set the left and right offset for this {@link ViewOffsetHelper}'s view.
+ *
+ * @param offset the offset in px.
+ * @return true if the offset has changed
+ */
+ public boolean setLeftAndRightOffset(int offset) {
+ if (mOffsetLeft != offset) {
+ mOffsetLeft = offset;
+ updateOffsets();
+ return true;
+ }
+ return false;
+ }
+
+ public int getTopAndBottomOffset() {
+ return mOffsetTop;
+ }
+
+ public int getLeftAndRightOffset() {
+ return mOffsetLeft;
+ }
+
+ public static final Property OFFSET_Y = new AnimUtils
+ .IntProperty("topAndBottomOffset") {
+
+ @Override
+ public void setValue(ViewOffsetHelper viewOffsetHelper, int offset) {
+ viewOffsetHelper.setTopAndBottomOffset(offset);
+ }
+
+ @Override
+ public Integer get(ViewOffsetHelper viewOffsetHelper) {
+ return viewOffsetHelper.getTopAndBottomOffset();
+ }
+ };
+}
diff --git a/app/src/main/java/io/plaidapp/util/ViewUtils.java b/app/src/main/java/io/plaidapp/util/ViewUtils.java
new file mode 100644
index 000000000..f34129d7a
--- /dev/null
+++ b/app/src/main/java/io/plaidapp/util/ViewUtils.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.plaidapp.util;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
+import android.os.Build;
+import android.support.annotation.ColorInt;
+import android.support.annotation.FloatRange;
+import android.support.annotation.NonNull;
+import android.text.TextPaint;
+import android.util.DisplayMetrics;
+import android.util.Property;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.ImageView;
+
+/**
+ * Utility methods for working with Views.
+ */
+public class ViewUtils {
+
+ private ViewUtils() { }
+
+ private static int actionBarSize = -1;
+
+ public static int getActionBarSize(Context context) {
+ if (actionBarSize < 0) {
+ TypedValue value = new TypedValue();
+ context.getTheme().resolveAttribute(android.R.attr.actionBarSize, value, true);
+ actionBarSize = TypedValue.complexToDimensionPixelSize(value.data, context
+ .getResources().getDisplayMetrics());
+ }
+ return actionBarSize;
+ }
+
+ public static RippleDrawable createRipple(@ColorInt int color,
+ @FloatRange(from = 0f, to = 1f) float alpha) {
+ color = ColorUtils.modifyAlpha(color, alpha);
+ return new RippleDrawable(ColorStateList.valueOf(color), null, null);
+ }
+
+ public static RippleDrawable createMaskedRipple(@ColorInt int color,
+ @FloatRange(from = 0f, to = 1f) float alpha) {
+ color = ColorUtils.modifyAlpha(color, alpha);
+ return new RippleDrawable(ColorStateList.valueOf(color), null, new ColorDrawable
+ (0xffffffff));
+ }
+
+ public static void setLightStatusBar(@NonNull View view) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ int flags = view.getSystemUiVisibility();
+ flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ view.setSystemUiVisibility(flags);
+ }
+ }
+
+ public static void clearLightStatusBar(@NonNull View view) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ int flags = view.getSystemUiVisibility();
+ flags &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ view.setSystemUiVisibility(flags);
+ }
+ }
+
+ /**
+ * Recursive binary search to find the best size for the text.
+ *
+ * Adapted from https://github.com/grantland/android-autofittextview
+ */
+ public static float getSingleLineTextSize(String text,
+ TextPaint paint,
+ float targetWidth,
+ float low,
+ float high,
+ float precision,
+ DisplayMetrics metrics) {
+ final float mid = (low + high) / 2.0f;
+
+ paint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, mid, metrics));
+ final float maxLineWidth = paint.measureText(text);
+
+ if ((high - low) < precision) {
+ return low;
+ } else if (maxLineWidth > targetWidth) {
+ return getSingleLineTextSize(text, paint, targetWidth, low, mid, precision, metrics);
+ } else if (maxLineWidth < targetWidth) {
+ return getSingleLineTextSize(text, paint, targetWidth, mid, high, precision, metrics);
+ } else {
+ return mid;
+ }
+ }
+
+ public static final Property BACKGROUND_COLOR
+ = new AnimUtils.IntProperty("backgroundColor") {
+
+ @Override
+ public void setValue(View view, int value) {
+ view.setBackgroundColor(value);
+ }
+
+ @Override
+ public Integer get(View view) {
+ Drawable d = view.getBackground();
+ if (d instanceof ColorDrawable) {
+ return ((ColorDrawable) d).getColor();
+ }
+ return Color.TRANSPARENT;
+ }
+ };
+
+ public static final Property IMAGE_ALPHA
+ = new AnimUtils.IntProperty("imageAlpha") {
+
+ @Override
+ public void setValue(ImageView imageView, int value) {
+ imageView.setImageAlpha(value);
+ }
+
+ @Override
+ public Integer get(ImageView imageView) {
+ return imageView.getImageAlpha();
+ }
+ };
+}
diff --git a/app/src/main/java/io/plaidapp/util/customtabs/CustomTabActivityHelper.java b/app/src/main/java/io/plaidapp/util/customtabs/CustomTabActivityHelper.java
new file mode 100644
index 000000000..828ef99df
--- /dev/null
+++ b/app/src/main/java/io/plaidapp/util/customtabs/CustomTabActivityHelper.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.plaidapp.util.customtabs;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.customtabs.CustomTabsClient;
+import android.support.customtabs.CustomTabsIntent;
+import android.support.customtabs.CustomTabsServiceConnection;
+import android.support.customtabs.CustomTabsSession;
+
+import java.util.List;
+
+/**
+ * This is a helper class to manage the connection to the Custom Tabs Service and
+ *
+ * Adapted from github.com/GoogleChrome/custom-tabs-client
+ */
+public class CustomTabActivityHelper {
+ private CustomTabsSession mCustomTabsSession;
+ private CustomTabsClient mClient;
+ private CustomTabsServiceConnection mConnection;
+ private ConnectionCallback mConnectionCallback;
+
+ /**
+ * Opens the URL on a Custom Tab if possible; otherwise falls back to opening it via
+ * {@code Intent.ACTION_VIEW}
+ *
+ * @param activity The host activity
+ * @param customTabsIntent a CustomTabsIntent to be used if Custom Tabs is available
+ * @param uri the Uri to be opened
+ */
+ public static void openCustomTab(Activity activity,
+ CustomTabsIntent customTabsIntent,
+ Uri uri) {
+ String packageName = CustomTabsHelper.getPackageNameToUse(activity);
+
+ // if we cant find a package name, it means there's no browser that supports
+ // Custom Tabs installed. So, we fallback to a view intent
+ if (packageName != null) {
+ customTabsIntent.intent.setPackage(packageName);
+ customTabsIntent.launchUrl(activity, uri);
+ } else {
+ activity.startActivity(new Intent(Intent.ACTION_VIEW, uri));
+ }
+ }
+
+ /**
+ * Binds the Activity to the Custom Tabs Service
+ * @param activity the activity to be bound to the service
+ */
+ public void bindCustomTabsService(Activity activity) {
+ if (mClient != null) return;
+
+ String packageName = CustomTabsHelper.getPackageNameToUse(activity);
+ if (packageName == null) return;
+ mConnection = new CustomTabsServiceConnection() {
+ @Override
+ public void onCustomTabsServiceConnected(ComponentName name, CustomTabsClient client) {
+ mClient = client;
+ mClient.warmup(0L);
+ if (mConnectionCallback != null) mConnectionCallback.onCustomTabsConnected();
+ //Initialize a session as soon as possible.
+ getSession();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mClient = null;
+ if (mConnectionCallback != null) mConnectionCallback.onCustomTabsDisconnected();
+ }
+ };
+ CustomTabsClient.bindCustomTabsService(activity, packageName, mConnection);
+ }
+
+ /**
+ * Unbinds the Activity from the Custom Tabs Service
+ * @param activity the activity that is bound to the service
+ */
+ public void unbindCustomTabsService(Activity activity) {
+ if (mConnection == null) return;
+ activity.unbindService(mConnection);
+ mClient = null;
+ mCustomTabsSession = null;
+ }
+
+ /**
+ * Creates or retrieves an exiting CustomTabsSession
+ *
+ * @return a CustomTabsSession
+ */
+ public CustomTabsSession getSession() {
+ if (mClient == null) {
+ mCustomTabsSession = null;
+ } else if (mCustomTabsSession == null) {
+ mCustomTabsSession = mClient.newSession(null);
+ }
+ return mCustomTabsSession;
+ }
+
+ /**
+ * Register a Callback to be called when connected or disconnected from the Custom Tabs Service
+ * @param connectionCallback
+ */
+ public void setConnectionCallback(ConnectionCallback connectionCallback) {
+ this.mConnectionCallback = connectionCallback;
+ }
+
+ /**
+ * @see {@link CustomTabsSession#mayLaunchUrl(Uri, Bundle, List)}
+ * @return true if call to mayLaunchUrl was accepted
+ */
+ public boolean mayLaunchUrl(Uri uri, Bundle extras, List otherLikelyBundles) {
+ if (mClient == null) return false;
+
+ CustomTabsSession session = getSession();
+ if (session == null) return false;
+
+ return session.mayLaunchUrl(uri, extras, otherLikelyBundles);
+ }
+
+ /**
+ * A Callback for when the service is connected or disconnected. Use those callbacks to
+ * handle UI changes when the service is connected or disconnected
+ */
+ public interface ConnectionCallback {
+ /**
+ * Called when the service is connected
+ */
+ void onCustomTabsConnected();
+
+ /**
+ * Called when the service is disconnected
+ */
+ void onCustomTabsDisconnected();
+ }
+
+}
diff --git a/app/src/main/java/io/plaidapp/util/customtabs/CustomTabsHelper.java b/app/src/main/java/io/plaidapp/util/customtabs/CustomTabsHelper.java
new file mode 100644
index 000000000..a0159d9ea
--- /dev/null
+++ b/app/src/main/java/io/plaidapp/util/customtabs/CustomTabsHelper.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.plaidapp.util.customtabs;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.support.customtabs.CustomTabsService;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class for Custom Tabs.
+ *
+ * Adapted from github.com/GoogleChrome/custom-tabs-client
+ */
+public class CustomTabsHelper {
+ private static final String TAG = "CustomTabsHelper";
+ static final String STABLE_PACKAGE = "com.android.chrome";
+ static final String BETA_PACKAGE = "com.chrome.beta";
+ static final String DEV_PACKAGE = "com.chrome.dev";
+ static final String LOCAL_PACKAGE = "com.google.android.apps.chrome";
+ private static final String EXTRA_CUSTOM_TABS_KEEP_ALIVE =
+ "android.support.customtabs.extra.KEEP_ALIVE";
+
+ private static String sPackageNameToUse;
+
+ private CustomTabsHelper() {}
+
+ public static void addKeepAliveExtra(Context context, Intent intent) {
+ Intent keepAliveIntent = new Intent().setClassName(
+ context.getPackageName(), KeepAliveService.class.getCanonicalName());
+ intent.putExtra(EXTRA_CUSTOM_TABS_KEEP_ALIVE, keepAliveIntent);
+ }
+
+ /**
+ * Goes through all apps that handle VIEW intents and have a warmup service. Picks
+ * the one chosen by the user if there is one, otherwise makes a best effort to return a
+ * valid package name.
+ *
+ * This is not threadsafe.
+ *
+ * @param context {@link Context} to use for accessing {@link PackageManager}.
+ * @return The package name recommended to use for connecting to custom tabs related components.
+ */
+ public static String getPackageNameToUse(Context context) {
+ if (sPackageNameToUse != null) return sPackageNameToUse;
+
+ PackageManager pm = context.getPackageManager();
+ // Get default VIEW intent handler.
+ Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com"));
+ ResolveInfo defaultViewHandlerInfo = pm.resolveActivity(activityIntent, 0);
+ String defaultViewHandlerPackageName = null;
+ if (defaultViewHandlerInfo != null) {
+ defaultViewHandlerPackageName = defaultViewHandlerInfo.activityInfo.packageName;
+ }
+
+ // Get all apps that can handle VIEW intents.
+ List resolvedActivityList = pm.queryIntentActivities(activityIntent, 0);
+ List packagesSupportingCustomTabs = new ArrayList<>();
+ for (ResolveInfo info : resolvedActivityList) {
+ Intent serviceIntent = new Intent();
+ serviceIntent.setAction(CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION);
+ serviceIntent.setPackage(info.activityInfo.packageName);
+ if (pm.resolveService(serviceIntent, 0) != null) {
+ packagesSupportingCustomTabs.add(info.activityInfo.packageName);
+ }
+ }
+
+ // Now packagesSupportingCustomTabs contains all apps that can handle both VIEW intents
+ // and service calls.
+ if (packagesSupportingCustomTabs.isEmpty()) {
+ sPackageNameToUse = null;
+ } else if (packagesSupportingCustomTabs.size() == 1) {
+ sPackageNameToUse = packagesSupportingCustomTabs.get(0);
+ } else if (!TextUtils.isEmpty(defaultViewHandlerPackageName)
+ && !hasSpecializedHandlerIntents(context, activityIntent)
+ && packagesSupportingCustomTabs.contains(defaultViewHandlerPackageName)) {
+ sPackageNameToUse = defaultViewHandlerPackageName;
+ } else if (packagesSupportingCustomTabs.contains(STABLE_PACKAGE)) {
+ sPackageNameToUse = STABLE_PACKAGE;
+ } else if (packagesSupportingCustomTabs.contains(BETA_PACKAGE)) {
+ sPackageNameToUse = BETA_PACKAGE;
+ } else if (packagesSupportingCustomTabs.contains(DEV_PACKAGE)) {
+ sPackageNameToUse = DEV_PACKAGE;
+ } else if (packagesSupportingCustomTabs.contains(LOCAL_PACKAGE)) {
+ sPackageNameToUse = LOCAL_PACKAGE;
+ }
+ return sPackageNameToUse;
+ }
+
+ /**
+ * Used to check whether there is a specialized handler for a given intent.
+ * @param intent The intent to check with.
+ * @return Whether there is a specialized handler for the given intent.
+ */
+ private static boolean hasSpecializedHandlerIntents(Context context, Intent intent) {
+ try {
+ PackageManager pm = context.getPackageManager();
+ List handlers = pm.queryIntentActivities(
+ intent,
+ PackageManager.GET_RESOLVED_FILTER);
+ if (handlers == null || handlers.size() == 0) {
+ return false;
+ }
+ for (ResolveInfo resolveInfo : handlers) {
+ IntentFilter filter = resolveInfo.filter;
+ if (filter == null) continue;
+ if (filter.countDataAuthorities() == 0 || filter.countDataPaths() == 0) continue;
+ if (resolveInfo.activityInfo == null) continue;
+ return true;
+ }
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Runtime exception while getting specialized handlers");
+ }
+ return false;
+ }
+
+ /**
+ * @return All possible chrome package names that provide custom tabs feature.
+ */
+ public static String[] getPackages() {
+ return new String[]{"", STABLE_PACKAGE, BETA_PACKAGE, DEV_PACKAGE, LOCAL_PACKAGE};
+ }
+}
diff --git a/app/src/main/java/io/plaidapp/util/customtabs/KeepAliveService.java b/app/src/main/java/io/plaidapp/util/customtabs/KeepAliveService.java
new file mode 100644
index 000000000..25401e2b1
--- /dev/null
+++ b/app/src/main/java/io/plaidapp/util/customtabs/KeepAliveService.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.plaidapp.util.customtabs;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+
+/**
+ * Empty service used by the custom tab to bind to, raising the application's importance.
+ *
+ * Adapted from github.com/GoogleChrome/custom-tabs-client
+ */
+public class KeepAliveService extends Service {
+ private static final Binder sBinder = new Binder();
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return sBinder;
+ }
+}
diff --git a/app/src/main/java/io/plaidapp/util/glide/CircleTransform.java b/app/src/main/java/io/plaidapp/util/glide/CircleTransform.java
new file mode 100644
index 000000000..0a35d5dda
--- /dev/null
+++ b/app/src/main/java/io/plaidapp/util/glide/CircleTransform.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.plaidapp.util.glide;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
+import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
+
+/**
+ * Created by http://stackoverflow.com/a/25806229/409481
+ */
+public class CircleTransform extends BitmapTransformation {
+
+ public CircleTransform(Context context) {
+ super(context);
+ }
+
+ private static Bitmap circleCrop(BitmapPool pool, Bitmap source) {
+ if (source == null) return null;
+
+ int size = Math.min(source.getWidth(), source.getHeight());
+ int x = (source.getWidth() - size) / 2;
+ int y = (source.getHeight() - size) / 2;
+
+ // TODO this could be acquired from the pool too
+ Bitmap squared = Bitmap.createBitmap(source, x, y, size, size);
+
+ Bitmap result = pool.get(size, size, Bitmap.Config.ARGB_8888);
+ if (result == null) {
+ result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+ }
+
+ Canvas canvas = new Canvas(result);
+ Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG | Paint
+ .ANTI_ALIAS_FLAG);
+ paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader
+ .TileMode.CLAMP));
+ float r = size / 2f;
+ canvas.drawCircle(r, r, r, paint);
+ return result;
+ }
+
+ @Override
+ protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
+ return circleCrop(pool, toTransform);
+ }
+
+ @Override
+ public String getId() {
+ return getClass().getName();
+ }
+}
diff --git a/app/src/main/java/io/plaidapp/util/glide/DribbbleTarget.java b/app/src/main/java/io/plaidapp/util/glide/DribbbleTarget.java
new file mode 100644
index 000000000..8deeb71bc
--- /dev/null
+++ b/app/src/main/java/io/plaidapp/util/glide/DribbbleTarget.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.plaidapp.util.glide;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.graphics.Palette;
+
+import com.bumptech.glide.load.resource.bitmap.GlideBitmapDrawable;
+import com.bumptech.glide.load.resource.drawable.GlideDrawable;
+import com.bumptech.glide.load.resource.gif.GifDrawable;
+import com.bumptech.glide.request.animation.GlideAnimation;
+import com.bumptech.glide.request.target.GlideDrawableImageViewTarget;
+
+import io.plaidapp.R;
+import io.plaidapp.ui.widget.BadgedFourThreeImageView;
+import io.plaidapp.util.ColorUtils;
+import io.plaidapp.util.ViewUtils;
+
+/**
+ *
+ */
+public class DribbbleTarget extends GlideDrawableImageViewTarget implements Palette
+ .PaletteAsyncListener {
+
+ private boolean playGifs;
+
+ public DribbbleTarget(BadgedFourThreeImageView view, boolean playGifs) {
+ super(view);
+ this.playGifs = playGifs;
+ }
+
+ @Override
+ public void onResourceReady(GlideDrawable resource, GlideAnimation super GlideDrawable>
+ animation) {
+ super.onResourceReady(resource, animation);
+ if (!playGifs) {
+ resource.stop();
+ }
+
+ BadgedFourThreeImageView badgedImageView = (BadgedFourThreeImageView) getView();
+ if (resource instanceof GlideBitmapDrawable) {
+ Palette.from(((GlideBitmapDrawable) resource).getBitmap())
+ .clearFilters()
+ .generate(this);
+ badgedImageView.showBadge(false);
+ } else if (resource instanceof GifDrawable) {
+ Bitmap image = ((GifDrawable) resource).getFirstFrame();
+ Palette.from(image).clearFilters().generate(this);
+ badgedImageView.showBadge(true);
+
+ // look at the corner to determine the gif badge color
+ int cornerSize = (int) (56 * getView().getContext().getResources().getDisplayMetrics
+ ().scaledDensity);
+ Bitmap corner = Bitmap.createBitmap(image,
+ image.getWidth() - cornerSize,
+ image.getHeight() - cornerSize,
+ cornerSize, cornerSize);
+ boolean isDark = ColorUtils.isDark(corner);
+ corner.recycle();
+ badgedImageView.setBadgeColor(ContextCompat.getColor(getView().getContext(),
+ isDark ? R.color.gif_badge_dark_image : R.color.gif_badge_light_image));
+ }
+ }
+
+ @Override
+ public void onStart() {
+ if (playGifs) {
+ super.onStart();
+ }
+ }
+
+ @Override
+ public void onStop() {
+ if (playGifs) {
+ super.onStop();
+ }
+ }
+
+ @Override
+ public void onGenerated(Palette palette) {
+ Drawable ripple = null;
+ // try the named swatches in preference order
+ if (palette.getVibrantSwatch() != null) {
+ ripple = ViewUtils.createRipple(palette.getVibrantSwatch().getRgb(), 0.25f);
+ } else if (palette.getLightVibrantSwatch() != null) {
+ ripple = ViewUtils.createRipple(palette.getLightVibrantSwatch().getRgb(), 0.5f);
+ } else if (palette.getDarkVibrantSwatch() != null) {
+ ripple = ViewUtils.createRipple(palette.getDarkVibrantSwatch().getRgb(), 0.25f);
+ } else if (palette.getMutedSwatch() != null) {
+ ripple = ViewUtils.createRipple(palette.getMutedSwatch().getRgb(), 0.25f);
+ } else if (palette.getLightMutedSwatch() != null) {
+ ripple = ViewUtils.createRipple(palette.getLightMutedSwatch().getRgb(), 0.5f);
+ } else if (palette.getDarkMutedSwatch() != null) {
+ ripple = ViewUtils.createRipple(palette.getDarkMutedSwatch().getRgb(), 0.25f);
+ } else {
+ // no swatches found, fall back to grey :(
+ ripple = getView().getContext().getDrawable(R.drawable.mid_grey_ripple);
+ }
+ ((BadgedFourThreeImageView) getView()).setForeground(ripple);
+ }
+}
diff --git a/app/src/main/java/io/plaidapp/util/glide/GlideConfiguration.java b/app/src/main/java/io/plaidapp/util/glide/GlideConfiguration.java
new file mode 100644
index 000000000..dcc94b497
--- /dev/null
+++ b/app/src/main/java/io/plaidapp/util/glide/GlideConfiguration.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.plaidapp.util.glide;
+
+import android.app.ActivityManager;
+import android.content.Context;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.GlideBuilder;
+import com.bumptech.glide.load.DecodeFormat;
+import com.bumptech.glide.module.GlideModule;
+
+/**
+ * Configure Glide to set desired image quality.
+ */
+public class GlideConfiguration implements GlideModule {
+
+ @Override
+ public void applyOptions(Context context, GlideBuilder builder) {
+ // Prefer higher quality images unless we're on a low RAM device
+ ActivityManager activityManager =
+ (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ builder.setDecodeFormat(activityManager.isLowRamDevice() ?
+ DecodeFormat.PREFER_RGB_565 : DecodeFormat.PREFER_ARGB_8888);
+ }
+
+ @Override
+ public void registerComponents(Context context, Glide glide) {
+
+ }
+}
diff --git a/app/src/main/java/io/plaidapp/util/glide/GlideUtils.java b/app/src/main/java/io/plaidapp/util/glide/GlideUtils.java
new file mode 100644
index 000000000..af8b08107
--- /dev/null
+++ b/app/src/main/java/io/plaidapp/util/glide/GlideUtils.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.plaidapp.util.glide;
+
+import android.graphics.Bitmap;
+
+import com.bumptech.glide.load.resource.bitmap.GlideBitmapDrawable;
+import com.bumptech.glide.load.resource.drawable.GlideDrawable;
+import com.bumptech.glide.load.resource.gif.GifDrawable;
+
+/**
+ * Utility methods for working with Glide.
+ */
+public class GlideUtils {
+
+ private GlideUtils() {
+ }
+
+ public static Bitmap getBitmap(GlideDrawable glideDrawable) {
+ if (glideDrawable instanceof GlideBitmapDrawable) {
+ return ((GlideBitmapDrawable) glideDrawable).getBitmap();
+ } else if (glideDrawable instanceof GifDrawable) {
+ return ((GifDrawable) glideDrawable).getFirstFrame();
+ }
+ return null;
+ }
+}
diff --git a/app/src/main/java/io/plaidapp/util/glide/ImageSpanTarget.java b/app/src/main/java/io/plaidapp/util/glide/ImageSpanTarget.java
new file mode 100644
index 000000000..64c0117b1
--- /dev/null
+++ b/app/src/main/java/io/plaidapp/util/glide/ImageSpanTarget.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.plaidapp.util.glide;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.ImageSpan;
+import android.transition.TransitionManager;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.bumptech.glide.request.animation.GlideAnimation;
+import com.bumptech.glide.request.target.SimpleTarget;
+
+import java.lang.ref.WeakReference;
+
+import in.uncod.android.bypass.style.ImageLoadingSpan;
+
+/**
+ * A target that puts a downloaded image into an ImageSpan in the provided TextView. It uses a
+ * {@link ImageLoadingSpan} to mark the area to be replaced by the image.
+ */
+public class ImageSpanTarget extends SimpleTarget {
+
+ private WeakReference textView;
+ private ImageLoadingSpan loadingSpan;
+
+ public ImageSpanTarget(TextView textView, ImageLoadingSpan loadingSpan) {
+ this.textView = new WeakReference<>(textView);
+ this.loadingSpan = loadingSpan;
+ }
+
+ @Override
+ public void onResourceReady(Bitmap bitmap, GlideAnimation super Bitmap> glideAnimation) {
+ TextView tv = textView.get();
+ if (tv != null) {
+ BitmapDrawable bitmapDrawable = new BitmapDrawable(tv.getResources(), bitmap);
+ // image span doesn't handle scaling so we manually set bounds
+ if (bitmap.getWidth() > tv.getWidth()) {
+ float aspectRatio = (float) bitmap.getHeight() / (float) bitmap.getWidth();
+ bitmapDrawable.setBounds(0, 0, tv.getWidth(), (int) (aspectRatio * tv.getWidth()));
+ } else {
+ bitmapDrawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
+ }
+ ImageSpan span = new ImageSpan(bitmapDrawable);
+ // add the image span and remove our marker
+ SpannableStringBuilder ssb = new SpannableStringBuilder(tv.getText());
+ int start = ssb.getSpanStart(loadingSpan);
+ int end = ssb.getSpanEnd(loadingSpan);
+ if (start >= 0 && end >= 0) {
+ ssb.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ ssb.removeSpan(loadingSpan);
+ // animate the change
+ TransitionManager.beginDelayedTransition((ViewGroup) tv.getParent());
+ tv.setText(ssb);
+ }
+ }
+
+}
diff --git a/app/src/main/res/anim/chrome_custom_tab_enter.xml b/app/src/main/res/anim/chrome_custom_tab_enter.xml
new file mode 100644
index 000000000..2ebc85eda
--- /dev/null
+++ b/app/src/main/res/anim/chrome_custom_tab_enter.xml
@@ -0,0 +1,26 @@
+
+
+
+
diff --git a/app/src/main/res/anim/fade_out_rapidly.xml b/app/src/main/res/anim/fade_out_rapidly.xml
new file mode 100644
index 000000000..882d59ff9
--- /dev/null
+++ b/app/src/main/res/anim/fade_out_rapidly.xml
@@ -0,0 +1,26 @@
+
+
+
+
diff --git a/app/src/main/res/anim/grid_enter.xml b/app/src/main/res/anim/grid_enter.xml
new file mode 100644
index 000000000..b77ae34e5
--- /dev/null
+++ b/app/src/main/res/anim/grid_enter.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/anim/layout_grid_enter.xml b/app/src/main/res/anim/layout_grid_enter.xml
new file mode 100644
index 000000000..f30aea2e7
--- /dev/null
+++ b/app/src/main/res/anim/layout_grid_enter.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/app/src/main/res/animator/app_bar_pin.xml b/app/src/main/res/animator/app_bar_pin.xml
new file mode 100644
index 000000000..2d3dab9d2
--- /dev/null
+++ b/app/src/main/res/animator/app_bar_pin.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+
+
diff --git a/app/src/main/res/animator/button_frown.xml b/app/src/main/res/animator/button_frown.xml
new file mode 100644
index 000000000..bf9e440c4
--- /dev/null
+++ b/app/src/main/res/animator/button_frown.xml
@@ -0,0 +1,26 @@
+
+
+
+
diff --git a/app/src/main/res/animator/comment_add_lines_1.xml b/app/src/main/res/animator/comment_add_lines_1.xml
new file mode 100644
index 000000000..e9db70b76
--- /dev/null
+++ b/app/src/main/res/animator/comment_add_lines_1.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/app/src/main/res/animator/comment_add_lines_2.xml b/app/src/main/res/animator/comment_add_lines_2.xml
new file mode 100644
index 000000000..e6dcab21a
--- /dev/null
+++ b/app/src/main/res/animator/comment_add_lines_2.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/app/src/main/res/animator/comment_add_lines_3.xml b/app/src/main/res/animator/comment_add_lines_3.xml
new file mode 100644
index 000000000..a89f4f1e7
--- /dev/null
+++ b/app/src/main/res/animator/comment_add_lines_3.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/app/src/main/res/animator/comment_expand_full_heart.xml b/app/src/main/res/animator/comment_expand_full_heart.xml
new file mode 100644
index 000000000..500c20321
--- /dev/null
+++ b/app/src/main/res/animator/comment_expand_full_heart.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/animator/comment_fade_empty_heart.xml b/app/src/main/res/animator/comment_fade_empty_heart.xml
new file mode 100644
index 000000000..acfaa4042
--- /dev/null
+++ b/app/src/main/res/animator/comment_fade_empty_heart.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/app/src/main/res/animator/comment_lines_add_1.xml b/app/src/main/res/animator/comment_lines_add_1.xml
new file mode 100644
index 000000000..0e25aeb96
--- /dev/null
+++ b/app/src/main/res/animator/comment_lines_add_1.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/app/src/main/res/animator/comment_lines_add_2.xml b/app/src/main/res/animator/comment_lines_add_2.xml
new file mode 100644
index 000000000..f22634597
--- /dev/null
+++ b/app/src/main/res/animator/comment_lines_add_2.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/app/src/main/res/animator/comment_lines_add_3.xml b/app/src/main/res/animator/comment_lines_add_3.xml
new file mode 100644
index 000000000..f9b64d52b
--- /dev/null
+++ b/app/src/main/res/animator/comment_lines_add_3.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/app/src/main/res/animator/comment_lines_add_rotate.xml b/app/src/main/res/animator/comment_lines_add_rotate.xml
new file mode 100644
index 000000000..d495858af
--- /dev/null
+++ b/app/src/main/res/animator/comment_lines_add_rotate.xml
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/app/src/main/res/animator/comment_selection.xml b/app/src/main/res/animator/comment_selection.xml
new file mode 100644
index 000000000..bc5c090a9
--- /dev/null
+++ b/app/src/main/res/animator/comment_selection.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+ -
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/animator/disable_text_entry.xml b/app/src/main/res/animator/disable_text_entry.xml
new file mode 100644
index 000000000..bd7ba91a1
--- /dev/null
+++ b/app/src/main/res/animator/disable_text_entry.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
diff --git a/app/src/main/res/animator/fab_empty_progress_left.xml b/app/src/main/res/animator/fab_empty_progress_left.xml
new file mode 100644
index 000000000..335c7263a
--- /dev/null
+++ b/app/src/main/res/animator/fab_empty_progress_left.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/animator/fab_empty_progress_right.xml b/app/src/main/res/animator/fab_empty_progress_right.xml
new file mode 100644
index 000000000..38e92619a
--- /dev/null
+++ b/app/src/main/res/animator/fab_empty_progress_right.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/animator/fab_hide_activity_circle.xml b/app/src/main/res/animator/fab_hide_activity_circle.xml
new file mode 100644
index 000000000..3738c54cf
--- /dev/null
+++ b/app/src/main/res/animator/fab_hide_activity_circle.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/animator/fab_load_progress.xml b/app/src/main/res/animator/fab_load_progress.xml
new file mode 100644
index 000000000..a2ea622d1
--- /dev/null
+++ b/app/src/main/res/animator/fab_load_progress.xml
@@ -0,0 +1,25 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/animator/fab_load_progress_rotate.xml b/app/src/main/res/animator/fab_load_progress_rotate.xml
new file mode 100644
index 000000000..b9ce3087c
--- /dev/null
+++ b/app/src/main/res/animator/fab_load_progress_rotate.xml
@@ -0,0 +1,25 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/animator/fab_show_activity_circle.xml b/app/src/main/res/animator/fab_show_activity_circle.xml
new file mode 100644
index 000000000..b7d6d8c5b
--- /dev/null
+++ b/app/src/main/res/animator/fab_show_activity_circle.xml
@@ -0,0 +1,25 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/animator/fab_show_progress_bar_emptying.xml b/app/src/main/res/animator/fab_show_progress_bar_emptying.xml
new file mode 100644
index 000000000..4c8403d5b
--- /dev/null
+++ b/app/src/main/res/animator/fab_show_progress_bar_emptying.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/animator/heart_empty.xml b/app/src/main/res/animator/heart_empty.xml
new file mode 100644
index 000000000..a42be5f57
--- /dev/null
+++ b/app/src/main/res/animator/heart_empty.xml
@@ -0,0 +1,25 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/animator/heart_fill.xml b/app/src/main/res/animator/heart_fill.xml
new file mode 100644
index 000000000..7f456028a
--- /dev/null
+++ b/app/src/main/res/animator/heart_fill.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/animator/raise.xml b/app/src/main/res/animator/raise.xml
new file mode 100644
index 000000000..5400c3898
--- /dev/null
+++ b/app/src/main/res/animator/raise.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+ -
+
+
+ -
+
+
+
diff --git a/app/src/main/res/animator/reply.xml b/app/src/main/res/animator/reply.xml
new file mode 100644
index 000000000..86e72c25e
--- /dev/null
+++ b/app/src/main/res/animator/reply.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/animator/searchback_hide_arrow_head.xml b/app/src/main/res/animator/searchback_hide_arrow_head.xml
new file mode 100644
index 000000000..9b4431543
--- /dev/null
+++ b/app/src/main/res/animator/searchback_hide_arrow_head.xml
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/app/src/main/res/animator/searchback_hide_circle.xml b/app/src/main/res/animator/searchback_hide_circle.xml
new file mode 100644
index 000000000..cd3709005
--- /dev/null
+++ b/app/src/main/res/animator/searchback_hide_circle.xml
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/app/src/main/res/animator/searchback_show_arrow_head.xml b/app/src/main/res/animator/searchback_show_arrow_head.xml
new file mode 100644
index 000000000..c700f2a3d
--- /dev/null
+++ b/app/src/main/res/animator/searchback_show_arrow_head.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/animator/searchback_show_circle.xml b/app/src/main/res/animator/searchback_show_circle.xml
new file mode 100644
index 000000000..bfaf0af2b
--- /dev/null
+++ b/app/src/main/res/animator/searchback_show_circle.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/animator/searchback_stem_back_to_search.xml b/app/src/main/res/animator/searchback_stem_back_to_search.xml
new file mode 100644
index 000000000..e2340fbd5
--- /dev/null
+++ b/app/src/main/res/animator/searchback_stem_back_to_search.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/animator/searchback_stem_search_to_back.xml b/app/src/main/res/animator/searchback_stem_search_to_back.xml
new file mode 100644
index 000000000..b45ea3155
--- /dev/null
+++ b/app/src/main/res/animator/searchback_stem_search_to_back.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/animator/show_connection_cross.xml b/app/src/main/res/animator/show_connection_cross.xml
new file mode 100644
index 000000000..a558c42a2
--- /dev/null
+++ b/app/src/main/res/animator/show_connection_cross.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/animator/show_connection_line.xml b/app/src/main/res/animator/show_connection_line.xml
new file mode 100644
index 000000000..73d18db79
--- /dev/null
+++ b/app/src/main/res/animator/show_connection_line.xml
@@ -0,0 +1,26 @@
+
+
+
+
diff --git a/app/src/main/res/color/designer_news_author.xml b/app/src/main/res/color/designer_news_author.xml
new file mode 100644
index 000000000..a4c7715f5
--- /dev/null
+++ b/app/src/main/res/color/designer_news_author.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/color/designer_news_button.xml b/app/src/main/res/color/designer_news_button.xml
new file mode 100644
index 000000000..51674f210
--- /dev/null
+++ b/app/src/main/res/color/designer_news_button.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/color/designer_news_links.xml b/app/src/main/res/color/designer_news_links.xml
new file mode 100644
index 000000000..916476d7b
--- /dev/null
+++ b/app/src/main/res/color/designer_news_links.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/color/dribbble_links.xml b/app/src/main/res/color/dribbble_links.xml
new file mode 100644
index 000000000..78a9c5d76
--- /dev/null
+++ b/app/src/main/res/color/dribbble_links.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/color/dribble_comment_author.xml b/app/src/main/res/color/dribble_comment_author.xml
new file mode 100644
index 000000000..90280e2c2
--- /dev/null
+++ b/app/src/main/res/color/dribble_comment_author.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/color/filter_text.xml b/app/src/main/res/color/filter_text.xml
new file mode 100644
index 000000000..907802266
--- /dev/null
+++ b/app/src/main/res/color/filter_text.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable-mdpi/grid_item_background.9.png b/app/src/main/res/drawable-mdpi/grid_item_background.9.png
new file mode 100644
index 000000000..37054150b
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/grid_item_background.9.png differ
diff --git a/app/src/main/res/drawable-v23/designer_news_item_background.xml b/app/src/main/res/drawable-v23/designer_news_item_background.xml
new file mode 100644
index 000000000..04ef98835
--- /dev/null
+++ b/app/src/main/res/drawable-v23/designer_news_item_background.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
diff --git a/app/src/main/res/drawable-v23/product_hunt_item_background.xml b/app/src/main/res/drawable-v23/product_hunt_item_background.xml
new file mode 100644
index 000000000..8c41785c5
--- /dev/null
+++ b/app/src/main/res/drawable-v23/product_hunt_item_background.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
diff --git a/app/src/main/res/drawable/avatar_placeholder.xml b/app/src/main/res/drawable/avatar_placeholder.xml
new file mode 100644
index 000000000..d199d499f
--- /dev/null
+++ b/app/src/main/res/drawable/avatar_placeholder.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/avd_back_to_search.xml b/app/src/main/res/drawable/avd_back_to_search.xml
new file mode 100644
index 000000000..3da6124b9
--- /dev/null
+++ b/app/src/main/res/drawable/avd_back_to_search.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/avd_no_connection.xml b/app/src/main/res/drawable/avd_no_connection.xml
new file mode 100644
index 000000000..3ef320be9
--- /dev/null
+++ b/app/src/main/res/drawable/avd_no_connection.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/avd_search_to_back.xml b/app/src/main/res/drawable/avd_search_to_back.xml
new file mode 100644
index 000000000..a3657878d
--- /dev/null
+++ b/app/src/main/res/drawable/avd_search_to_back.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/comment_background.xml b/app/src/main/res/drawable/comment_background.xml
new file mode 100644
index 000000000..0cf5898a9
--- /dev/null
+++ b/app/src/main/res/drawable/comment_background.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/comment_heart.xml b/app/src/main/res/drawable/comment_heart.xml
new file mode 100644
index 000000000..68992fbe7
--- /dev/null
+++ b/app/src/main/res/drawable/comment_heart.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/designer_news_app_bar_background.xml b/app/src/main/res/drawable/designer_news_app_bar_background.xml
new file mode 100644
index 000000000..bdb2c29c8
--- /dev/null
+++ b/app/src/main/res/drawable/designer_news_app_bar_background.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/designer_news_custom_tab_placeholder.xml b/app/src/main/res/drawable/designer_news_custom_tab_placeholder.xml
new file mode 100644
index 000000000..1f5beb242
--- /dev/null
+++ b/app/src/main/res/drawable/designer_news_custom_tab_placeholder.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+ -
+
+
+
+
+
diff --git a/app/src/main/res/drawable/designer_news_item_background.xml b/app/src/main/res/drawable/designer_news_item_background.xml
new file mode 100644
index 000000000..6dccc4b91
--- /dev/null
+++ b/app/src/main/res/drawable/designer_news_item_background.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/dialog_background.xml b/app/src/main/res/drawable/dialog_background.xml
new file mode 100644
index 000000000..18553f217
--- /dev/null
+++ b/app/src/main/res/drawable/dialog_background.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/dribbble_logo.xml b/app/src/main/res/drawable/dribbble_logo.xml
new file mode 100644
index 000000000..bd2afe4cb
--- /dev/null
+++ b/app/src/main/res/drawable/dribbble_logo.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/fab.xml b/app/src/main/res/drawable/fab.xml
new file mode 100644
index 000000000..0d4adaeba
--- /dev/null
+++ b/app/src/main/res/drawable/fab.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/fab_dribbble_fav.xml b/app/src/main/res/drawable/fab_dribbble_fav.xml
new file mode 100644
index 000000000..444f16084
--- /dev/null
+++ b/app/src/main/res/drawable/fab_dribbble_fav.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/fab_heart.xml b/app/src/main/res/drawable/fab_heart.xml
new file mode 100644
index 000000000..48d42dd27
--- /dev/null
+++ b/app/src/main/res/drawable/fab_heart.xml
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/filter_placeholder.xml b/app/src/main/res/drawable/filter_placeholder.xml
new file mode 100644
index 000000000..5c2a2a7f3
--- /dev/null
+++ b/app/src/main/res/drawable/filter_placeholder.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/heart_anim.xml b/app/src/main/res/drawable/heart_anim.xml
new file mode 100644
index 000000000..566b59ddd
--- /dev/null
+++ b/app/src/main/res/drawable/heart_anim.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/heart_comment_anim.xml b/app/src/main/res/drawable/heart_comment_anim.xml
new file mode 100644
index 000000000..5735dd83d
--- /dev/null
+++ b/app/src/main/res/drawable/heart_comment_anim.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_action_share_24dp.xml b/app/src/main/res/drawable/ic_action_share_24dp.xml
new file mode 100644
index 000000000..f2b45fe0f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_action_share_24dp.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_add_comment.xml b/app/src/main/res/drawable/ic_add_comment.xml
new file mode 100644
index 000000000..5061bc2d7
--- /dev/null
+++ b/app/src/main/res/drawable/ic_add_comment.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_add_comment_state.xml b/app/src/main/res/drawable/ic_add_comment_state.xml
new file mode 100644
index 000000000..78b0fecd6
--- /dev/null
+++ b/app/src/main/res/drawable/ic_add_comment_state.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_add_dark.xml b/app/src/main/res/drawable/ic_add_dark.xml
new file mode 100644
index 000000000..60dad2913
--- /dev/null
+++ b/app/src/main/res/drawable/ic_add_dark.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_add_light.xml b/app/src/main/res/drawable/ic_add_light.xml
new file mode 100644
index 000000000..7d1393901
--- /dev/null
+++ b/app/src/main/res/drawable/ic_add_light.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_arrow_back.xml b/app/src/main/res/drawable/ic_arrow_back.xml
new file mode 100644
index 000000000..b69c3cf98
--- /dev/null
+++ b/app/src/main/res/drawable/ic_arrow_back.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_arrow_back_padded.xml b/app/src/main/res/drawable/ic_arrow_back_padded.xml
new file mode 100644
index 000000000..2566836cc
--- /dev/null
+++ b/app/src/main/res/drawable/ic_arrow_back_padded.xml
@@ -0,0 +1,21 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_comment_dark.xml b/app/src/main/res/drawable/ic_comment_dark.xml
new file mode 100644
index 000000000..039f25630
--- /dev/null
+++ b/app/src/main/res/drawable/ic_comment_dark.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_comment_light.xml b/app/src/main/res/drawable/ic_comment_light.xml
new file mode 100644
index 000000000..567a9a124
--- /dev/null
+++ b/app/src/main/res/drawable/ic_comment_light.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_comment_lines.xml b/app/src/main/res/drawable/ic_comment_lines.xml
new file mode 100644
index 000000000..549e754a1
--- /dev/null
+++ b/app/src/main/res/drawable/ic_comment_lines.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_designer_news.xml b/app/src/main/res/drawable/ic_designer_news.xml
new file mode 100644
index 000000000..93bb05230
--- /dev/null
+++ b/app/src/main/res/drawable/ic_designer_news.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_dribbble.xml b/app/src/main/res/drawable/ic_dribbble.xml
new file mode 100644
index 000000000..101d5edd6
--- /dev/null
+++ b/app/src/main/res/drawable/ic_dribbble.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_edit.xml b/app/src/main/res/drawable/ic_edit.xml
new file mode 100644
index 000000000..f5c817ee0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_edit.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_filter.xml b/app/src/main/res/drawable/ic_filter.xml
new file mode 100644
index 000000000..50e6310fe
--- /dev/null
+++ b/app/src/main/res/drawable/ic_filter.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_filter_small.xml b/app/src/main/res/drawable/ic_filter_small.xml
new file mode 100644
index 000000000..6f014886a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_filter_small.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_hacker_news.xml b/app/src/main/res/drawable/ic_hacker_news.xml
new file mode 100644
index 000000000..e1e87a3ea
--- /dev/null
+++ b/app/src/main/res/drawable/ic_hacker_news.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_heart_empty_24dp.xml b/app/src/main/res/drawable/ic_heart_empty_24dp.xml
new file mode 100644
index 000000000..4e0f8586c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_heart_empty_24dp.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_heart_empty_56dp.xml b/app/src/main/res/drawable/ic_heart_empty_56dp.xml
new file mode 100644
index 000000000..0ff83a972
--- /dev/null
+++ b/app/src/main/res/drawable/ic_heart_empty_56dp.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_heart_full_24dp_grey.xml b/app/src/main/res/drawable/ic_heart_full_24dp_grey.xml
new file mode 100644
index 000000000..55dcaa1df
--- /dev/null
+++ b/app/src/main/res/drawable/ic_heart_full_24dp_grey.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_heart_full_24dp_pink.xml b/app/src/main/res/drawable/ic_heart_full_24dp_pink.xml
new file mode 100644
index 000000000..d5a2c78ea
--- /dev/null
+++ b/app/src/main/res/drawable/ic_heart_full_24dp_pink.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_heart_full_56dp.xml b/app/src/main/res/drawable/ic_heart_full_56dp.xml
new file mode 100644
index 000000000..081abbcf8
--- /dev/null
+++ b/app/src/main/res/drawable/ic_heart_full_56dp.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_news.xml b/app/src/main/res/drawable/ic_news.xml
new file mode 100644
index 000000000..a873ddc64
--- /dev/null
+++ b/app/src/main/res/drawable/ic_news.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_no_comments.xml b/app/src/main/res/drawable/ic_no_comments.xml
new file mode 100644
index 000000000..461ac8b00
--- /dev/null
+++ b/app/src/main/res/drawable/ic_no_comments.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_player.xml b/app/src/main/res/drawable/ic_player.xml
new file mode 100644
index 000000000..52cec2c51
--- /dev/null
+++ b/app/src/main/res/drawable/ic_player.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_pocket.xml b/app/src/main/res/drawable/ic_pocket.xml
new file mode 100644
index 000000000..5cefe9612
--- /dev/null
+++ b/app/src/main/res/drawable/ic_pocket.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_product_hunt.xml b/app/src/main/res/drawable/ic_product_hunt.xml
new file mode 100644
index 000000000..af22f0f1f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_product_hunt.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_reply.xml b/app/src/main/res/drawable/ic_reply.xml
new file mode 100644
index 000000000..1ac8b59ba
--- /dev/null
+++ b/app/src/main/res/drawable/ic_reply.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_save_24dp.xml b/app/src/main/res/drawable/ic_save_24dp.xml
new file mode 100644
index 000000000..3df8667e0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_save_24dp.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_search_24dp.xml b/app/src/main/res/drawable/ic_search_24dp.xml
new file mode 100644
index 000000000..c3fa56e09
--- /dev/null
+++ b/app/src/main/res/drawable/ic_search_24dp.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_thumb_up.xml b/app/src/main/res/drawable/ic_thumb_up.xml
new file mode 100644
index 000000000..afca5f67e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_thumb_up.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_views_24dp.xml b/app/src/main/res/drawable/ic_views_24dp.xml
new file mode 100644
index 000000000..2a77fb5d2
--- /dev/null
+++ b/app/src/main/res/drawable/ic_views_24dp.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/light_ripple.xml b/app/src/main/res/drawable/light_ripple.xml
new file mode 100644
index 000000000..15938ce0f
--- /dev/null
+++ b/app/src/main/res/drawable/light_ripple.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/list_divider.xml b/app/src/main/res/drawable/list_divider.xml
new file mode 100644
index 000000000..3b2f9e74d
--- /dev/null
+++ b/app/src/main/res/drawable/list_divider.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/mid_grey_bounded_ripple.xml b/app/src/main/res/drawable/mid_grey_bounded_ripple.xml
new file mode 100644
index 000000000..11e8411fb
--- /dev/null
+++ b/app/src/main/res/drawable/mid_grey_bounded_ripple.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/mid_grey_ripple.xml b/app/src/main/res/drawable/mid_grey_ripple.xml
new file mode 100644
index 000000000..f80db08a0
--- /dev/null
+++ b/app/src/main/res/drawable/mid_grey_ripple.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/no_connection.xml b/app/src/main/res/drawable/no_connection.xml
new file mode 100644
index 000000000..6492b458c
--- /dev/null
+++ b/app/src/main/res/drawable/no_connection.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/post_story.xml b/app/src/main/res/drawable/post_story.xml
new file mode 100644
index 000000000..a9ab53a72
--- /dev/null
+++ b/app/src/main/res/drawable/post_story.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+ -
+
+
+
+
+
diff --git a/app/src/main/res/drawable/product_hunt_item_background.xml b/app/src/main/res/drawable/product_hunt_item_background.xml
new file mode 100644
index 000000000..db4de46d8
--- /dev/null
+++ b/app/src/main/res/drawable/product_hunt_item_background.xml
@@ -0,0 +1,22 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/searchback_back.xml b/app/src/main/res/drawable/searchback_back.xml
new file mode 100644
index 000000000..acf738b82
--- /dev/null
+++ b/app/src/main/res/drawable/searchback_back.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/searchback_search.xml b/app/src/main/res/drawable/searchback_search.xml
new file mode 100644
index 000000000..bad5ac37a
--- /dev/null
+++ b/app/src/main/res/drawable/searchback_search.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout-land/activity_designer_news_story.xml b/app/src/main/res/layout-land/activity_designer_news_story.xml
new file mode 100644
index 000000000..5f8acfb58
--- /dev/null
+++ b/app/src/main/res/layout-land/activity_designer_news_story.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout-land/designer_news_story_description.xml b/app/src/main/res/layout-land/designer_news_story_description.xml
new file mode 100644
index 000000000..7ceda433b
--- /dev/null
+++ b/app/src/main/res/layout-land/designer_news_story_description.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-v23/dribbble_shot_title.xml b/app/src/main/res/layout-v23/dribbble_shot_title.xml
new file mode 100644
index 000000000..d7c84084e
--- /dev/null
+++ b/app/src/main/res/layout-v23/dribbble_shot_title.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/account_dropdown_item.xml b/app/src/main/res/layout/account_dropdown_item.xml
new file mode 100644
index 000000000..9287901da
--- /dev/null
+++ b/app/src/main/res/layout/account_dropdown_item.xml
@@ -0,0 +1,29 @@
+
+
+
+
diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml
new file mode 100644
index 000000000..a5c0044bd
--- /dev/null
+++ b/app/src/main/res/layout/activity_about.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_designer_news_login.xml b/app/src/main/res/layout/activity_designer_news_login.xml
new file mode 100644
index 000000000..4f8b07c1b
--- /dev/null
+++ b/app/src/main/res/layout/activity_designer_news_login.xml
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_designer_news_story.xml b/app/src/main/res/layout/activity_designer_news_story.xml
new file mode 100644
index 000000000..0a184e0f8
--- /dev/null
+++ b/app/src/main/res/layout/activity_designer_news_story.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_dribbble_login.xml b/app/src/main/res/layout/activity_dribbble_login.xml
new file mode 100644
index 000000000..0aa8c0432
--- /dev/null
+++ b/app/src/main/res/layout/activity_dribbble_login.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_dribbble_shot.xml b/app/src/main/res/layout/activity_dribbble_shot.xml
new file mode 100644
index 000000000..59971cbc2
--- /dev/null
+++ b/app/src/main/res/layout/activity_dribbble_shot.xml
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml
new file mode 100644
index 000000000..9750256ef
--- /dev/null
+++ b/app/src/main/res/layout/activity_home.xml
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_post_new_designer_news_story.xml b/app/src/main/res/layout/activity_post_new_designer_news_story.xml
new file mode 100644
index 000000000..5d9a03369
--- /dev/null
+++ b/app/src/main/res/layout/activity_post_new_designer_news_story.xml
@@ -0,0 +1,161 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml
new file mode 100644
index 000000000..cbde3b00a
--- /dev/null
+++ b/app/src/main/res/layout/activity_search.xml
@@ -0,0 +1,186 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/designer_news_comment.xml b/app/src/main/res/layout/designer_news_comment.xml
new file mode 100644
index 000000000..7892f2e65
--- /dev/null
+++ b/app/src/main/res/layout/designer_news_comment.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/designer_news_no_comments.xml b/app/src/main/res/layout/designer_news_no_comments.xml
new file mode 100644
index 000000000..ec0b5a65d
--- /dev/null
+++ b/app/src/main/res/layout/designer_news_no_comments.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/designer_news_story_description.xml b/app/src/main/res/layout/designer_news_story_description.xml
new file mode 100644
index 000000000..ebaee0007
--- /dev/null
+++ b/app/src/main/res/layout/designer_news_story_description.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/designer_news_story_fab.xml b/app/src/main/res/layout/designer_news_story_fab.xml
new file mode 100644
index 000000000..50a31cac3
--- /dev/null
+++ b/app/src/main/res/layout/designer_news_story_fab.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/designer_news_story_item.xml b/app/src/main/res/layout/designer_news_story_item.xml
new file mode 100644
index 000000000..b14f455bf
--- /dev/null
+++ b/app/src/main/res/layout/designer_news_story_item.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dribbble_comment.xml b/app/src/main/res/layout/dribbble_comment.xml
new file mode 100644
index 000000000..1951aef32
--- /dev/null
+++ b/app/src/main/res/layout/dribbble_comment.xml
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dribbble_enter_comment.xml b/app/src/main/res/layout/dribbble_enter_comment.xml
new file mode 100644
index 000000000..64a6f15ef
--- /dev/null
+++ b/app/src/main/res/layout/dribbble_enter_comment.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dribbble_no_comments.xml b/app/src/main/res/layout/dribbble_no_comments.xml
new file mode 100644
index 000000000..ac99b41b5
--- /dev/null
+++ b/app/src/main/res/layout/dribbble_no_comments.xml
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/app/src/main/res/layout/dribbble_shot_description.xml b/app/src/main/res/layout/dribbble_shot_description.xml
new file mode 100644
index 000000000..df408033b
--- /dev/null
+++ b/app/src/main/res/layout/dribbble_shot_description.xml
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dribbble_shot_item.xml b/app/src/main/res/layout/dribbble_shot_item.xml
new file mode 100644
index 000000000..95a758502
--- /dev/null
+++ b/app/src/main/res/layout/dribbble_shot_item.xml
@@ -0,0 +1,29 @@
+
+
+
+
diff --git a/app/src/main/res/layout/dribbble_shot_title.xml b/app/src/main/res/layout/dribbble_shot_title.xml
new file mode 100644
index 000000000..48944d879
--- /dev/null
+++ b/app/src/main/res/layout/dribbble_shot_title.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/filter_item.xml b/app/src/main/res/layout/filter_item.xml
new file mode 100644
index 000000000..925420134
--- /dev/null
+++ b/app/src/main/res/layout/filter_item.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/hacker_news_post_item.xml b/app/src/main/res/layout/hacker_news_post_item.xml
new file mode 100644
index 000000000..7827862d8
--- /dev/null
+++ b/app/src/main/res/layout/hacker_news_post_item.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/infinite_loading.xml b/app/src/main/res/layout/infinite_loading.xml
new file mode 100644
index 000000000..7c7574118
--- /dev/null
+++ b/app/src/main/res/layout/infinite_loading.xml
@@ -0,0 +1,28 @@
+
+
+
+
diff --git a/app/src/main/res/layout/loading.xml b/app/src/main/res/layout/loading.xml
new file mode 100644
index 000000000..09450d0b7
--- /dev/null
+++ b/app/src/main/res/layout/loading.xml
@@ -0,0 +1,27 @@
+
+
+
+
diff --git a/app/src/main/res/layout/no_connection.xml b/app/src/main/res/layout/no_connection.xml
new file mode 100644
index 000000000..dd6a8d484
--- /dev/null
+++ b/app/src/main/res/layout/no_connection.xml
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/app/src/main/res/layout/no_filters.xml b/app/src/main/res/layout/no_filters.xml
new file mode 100644
index 000000000..c9ae50b8d
--- /dev/null
+++ b/app/src/main/res/layout/no_filters.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/app/src/main/res/layout/no_search_results.xml b/app/src/main/res/layout/no_search_results.xml
new file mode 100644
index 000000000..61faba866
--- /dev/null
+++ b/app/src/main/res/layout/no_search_results.xml
@@ -0,0 +1,29 @@
+
+
+
+
diff --git a/app/src/main/res/layout/product_hunt_item.xml b/app/src/main/res/layout/product_hunt_item.xml
new file mode 100644
index 000000000..5b5682d79
--- /dev/null
+++ b/app/src/main/res/layout/product_hunt_item.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/toast_logged_in_confirmation.xml b/app/src/main/res/layout/toast_logged_in_confirmation.xml
new file mode 100644
index 000000000..0483e832b
--- /dev/null
+++ b/app/src/main/res/layout/toast_logged_in_confirmation.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/main.xml b/app/src/main/res/menu/main.xml
new file mode 100644
index 000000000..a50a03b55
--- /dev/null
+++ b/app/src/main/res/menu/main.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..b794296c7
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..b64f2c827
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..3b2277dd2
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..eac985b5e
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..cc32e358d
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/transition/auto.xml b/app/src/main/res/transition/auto.xml
new file mode 100644
index 000000000..b9224a1bc
--- /dev/null
+++ b/app/src/main/res/transition/auto.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
diff --git a/app/src/main/res/transition/designer_news_story_shared_enter.xml b/app/src/main/res/transition/designer_news_story_shared_enter.xml
new file mode 100644
index 000000000..65b951bcc
--- /dev/null
+++ b/app/src/main/res/transition/designer_news_story_shared_enter.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/transition/designer_news_story_shared_return.xml b/app/src/main/res/transition/designer_news_story_shared_return.xml
new file mode 100644
index 000000000..e49bcd730
--- /dev/null
+++ b/app/src/main/res/transition/designer_news_story_shared_return.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/transition/dribbble_shot_enter.xml b/app/src/main/res/transition/dribbble_shot_enter.xml
new file mode 100644
index 000000000..fca422ba8
--- /dev/null
+++ b/app/src/main/res/transition/dribbble_shot_enter.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/transition/dribbble_shot_return.xml b/app/src/main/res/transition/dribbble_shot_return.xml
new file mode 100644
index 000000000..5bd672fdd
--- /dev/null
+++ b/app/src/main/res/transition/dribbble_shot_return.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/transition/dribbble_shot_shared_enter.xml b/app/src/main/res/transition/dribbble_shot_shared_enter.xml
new file mode 100644
index 000000000..3ffcfc59c
--- /dev/null
+++ b/app/src/main/res/transition/dribbble_shot_shared_enter.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/transition/dribbble_shot_shared_return.xml b/app/src/main/res/transition/dribbble_shot_shared_return.xml
new file mode 100644
index 000000000..7c0e676b1
--- /dev/null
+++ b/app/src/main/res/transition/dribbble_shot_shared_return.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/transition/main_enter.xml b/app/src/main/res/transition/main_enter.xml
new file mode 100644
index 000000000..270253503
--- /dev/null
+++ b/app/src/main/res/transition/main_enter.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/transition/search_enter.xml b/app/src/main/res/transition/search_enter.xml
new file mode 100644
index 000000000..0531eaa27
--- /dev/null
+++ b/app/src/main/res/transition/search_enter.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values-sw600dp/dimens.xml b/app/src/main/res/values-sw600dp/dimens.xml
new file mode 100644
index 000000000..2a3c70046
--- /dev/null
+++ b/app/src/main/res/values-sw600dp/dimens.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+ 16dp
+ 8dp
+ 24dp
+ 80dp
+ 9dp
+
+
+ 56dp
+
+
diff --git a/app/src/main/res/values-w360dp/dimens.xml b/app/src/main/res/values-w360dp/dimens.xml
new file mode 100644
index 000000000..ebdad44a9
--- /dev/null
+++ b/app/src/main/res/values-w360dp/dimens.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+ 304dp
+
+
diff --git a/app/src/main/res/values-w480dp/dimens.xml b/app/src/main/res/values-w480dp/dimens.xml
new file mode 100644
index 000000000..b5417e8cc
--- /dev/null
+++ b/app/src/main/res/values-w480dp/dimens.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+ 480dp
+
+
diff --git a/app/src/main/res/values-w540dp/dimens.xml b/app/src/main/res/values-w540dp/dimens.xml
new file mode 100644
index 000000000..1ca212b2f
--- /dev/null
+++ b/app/src/main/res/values-w540dp/dimens.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+ 3
+
+
diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 000000000..c3fd9bdc6
--- /dev/null
+++ b/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,22 @@
+
+
+
+
+ 64dp
+
diff --git a/app/src/main/res/values/attrs_author_text_view.xml b/app/src/main/res/values/attrs_author_text_view.xml
new file mode 100644
index 000000000..90b4b08fd
--- /dev/null
+++ b/app/src/main/res/values/attrs_author_text_view.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/attrs_badged_image_view.xml b/app/src/main/res/values/attrs_badged_image_view.xml
new file mode 100644
index 000000000..9c45390b6
--- /dev/null
+++ b/app/src/main/res/values/attrs_badged_image_view.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/attrs_baseline_grid_text_view.xml b/app/src/main/res/values/attrs_baseline_grid_text_view.xml
new file mode 100644
index 000000000..c41dbcf10
--- /dev/null
+++ b/app/src/main/res/values/attrs_baseline_grid_text_view.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/attrs_collapsing_title_layout.xml b/app/src/main/res/values/attrs_collapsing_title_layout.xml
new file mode 100644
index 000000000..6932b6a69
--- /dev/null
+++ b/app/src/main/res/values/attrs_collapsing_title_layout.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/attrs_cutout_text_view.xml b/app/src/main/res/values/attrs_cutout_text_view.xml
new file mode 100644
index 000000000..66ab0dc8e
--- /dev/null
+++ b/app/src/main/res/values/attrs_cutout_text_view.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/attrs_drag_dismissables.xml b/app/src/main/res/values/attrs_drag_dismissables.xml
new file mode 100644
index 000000000..2f2fe1f99
--- /dev/null
+++ b/app/src/main/res/values/attrs_drag_dismissables.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/attrs_dynamic_text_view.xml b/app/src/main/res/values/attrs_dynamic_text_view.xml
new file mode 100644
index 000000000..b404a944f
--- /dev/null
+++ b/app/src/main/res/values/attrs_dynamic_text_view.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/attrs_fab_overlap_text_view.xml b/app/src/main/res/values/attrs_fab_overlap_text_view.xml
new file mode 100644
index 000000000..3194170d1
--- /dev/null
+++ b/app/src/main/res/values/attrs_fab_overlap_text_view.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/attrs_font.xml b/app/src/main/res/values/attrs_font.xml
new file mode 100644
index 000000000..10062b3a3
--- /dev/null
+++ b/app/src/main/res/values/attrs_font.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/attrs_font_text_view.xml b/app/src/main/res/values/attrs_font_text_view.xml
new file mode 100644
index 000000000..1595f319e
--- /dev/null
+++ b/app/src/main/res/values/attrs_font_text_view.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/attrs_foreground_view.xml b/app/src/main/res/values/attrs_foreground_view.xml
new file mode 100644
index 000000000..cb212717e
--- /dev/null
+++ b/app/src/main/res/values/attrs_foreground_view.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/attrs_parallax_scrimage_view.xml b/app/src/main/res/values/attrs_parallax_scrimage_view.xml
new file mode 100644
index 000000000..107bc36dc
--- /dev/null
+++ b/app/src/main/res/values/attrs_parallax_scrimage_view.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/attrs_pinnable.xml b/app/src/main/res/values/attrs_pinnable.xml
new file mode 100644
index 000000000..f5396b518
--- /dev/null
+++ b/app/src/main/res/values/attrs_pinnable.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 000000000..ee9c911c6
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+ #69F0AE
+ #69F0AE
+ #11261c
+ #8f69F0AE
+
+
+ #de000000
+ #8a000000
+ #ffffffff
+ #b3ffffff
+ #4dffffff
+ #99ffffff
+ @color/mid_grey
+ #fafafa
+ #ff333333
+ #99000000
+ #ff676767
+ #ff292929
+ #99323232
+ #40808080
+ #fff5f5f5
+ #ffE0E0E0
+ #ffe0e0e0
+ #99000000
+ #1f000000
+
+
+ #43000000
+ #8f000000
+
+
+
+ #03a9f4 -->
+ #039be5
+ #b303a9f4
+
+
+
+
+
+
+ #000133
+ #ffeceef1
+ #73000000
+
+
+
+ #fff06292
+ #ec407a
+ #ff33292d
+ #b3f06292
+ @color/text_primary_light
+ #deffffff
+ #ffffffff
+ #40000000
+ #b3ffffff
+
+
+ #ffe48529
+
+
+ #e65100
+ #ffcc80
+
+
+ #EF6C00
+
+
+
+
+ #33000000
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/comment_lines_add.xml b/app/src/main/res/values/comment_lines_add.xml
new file mode 100644
index 000000000..1522aeedd
--- /dev/null
+++ b/app/src/main/res/values/comment_lines_add.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+ M2,5 C2,3.9 2.9,3 4,3 L20,3 C21.1,3 22,3.9 22,5 L22,17 C22,18.1 21.1,19 20,19 L2,19 L2,5 Z M2,23 L6,19 L2,19 L2,23 Z
+ M7,8 L17,8
+ M7,11 L17,11
+ M7,14 L17,14
+ M12,7 L12,10
+ M8,11 L16,11
+ M12,12 L12,15
+ 24
+ 24
+ 12
+ 11
+
+
+ comment_line_1
+ comment_line_2
+ comment_line_3
+ comment_line_add_group
+
+
+ #777
+ #fbfbfb
+ 2
+
+
+ 300
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 000000000..201a03806
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+ 8dp
+ 4dp
+ 16dp
+ 32dp
+ 48dp
+ 64dp
+ 56dp
+ 72dp
+ 112sp
+ 1px
+ 2dp
+ 88dp
+ 72dp
+ 44dp
+
+
+ 24dp
+ 20dp
+
+
+ @dimen/match_parent
+
+
+ 2
+ 40dp
+
+
+ 128dp
+ 4dp
+ 4dp
+ 16dp
+
+
+ 168dp
+ 112dp
+
+ 40dp
+ 48dp
+ 4dp
+ 12dp
+ 40dp
+ -8dp
+
+
+ @dimen/match_parent
+
+
+ 4dp
+ 2dp
+ 6dp
+ 6dp
+ 8dp
+ 9dp
+ 16dp
+ 24dp
+
+
+ - -1
+
+
diff --git a/app/src/main/res/values/dribbble_fav.xml b/app/src/main/res/values/dribbble_fav.xml
new file mode 100644
index 000000000..d8982ab76
--- /dev/null
+++ b/app/src/main/res/values/dribbble_fav.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+ M32.95,19 C31.036,19 29.199,19.8828338 28,21.2724796 C26.801,19.8828338 24.964,19 23.05,19 C19.6565,19 17,21.6321526 17,24.9945504 C17,29.1089918 20.74,32.4713896 26.405,37.5667575 L28,39 L29.595,37.5667575 C35.26,32.4713896 39,29.1089918 39,24.9945504 C39,21.6321526 36.3435,19 32.95,19 L32.95,19 Z M28.1155,35.9536785 L28,36.0572207 L27.8845,35.9536785 C22.654,31.2506812 19.2,28.1444142 19.2,24.9945504 C19.2,22.8201635 20.8555,21.1798365 23.05,21.1798365 C24.744,21.1798365 26.394,22.2643052 26.9715,23.7520436 L29.023,23.7520436 C29.606,22.2643052 31.256,21.1798365 32.95,21.1798365 C35.1445,21.1798365 36.8,22.8201635 36.8,24.9945504 C36.8,28.1444142 33.346,31.2506812 28.1155,35.9536785 L28.1155,35.9536785 Z
+ M28,39 L26.405,37.5667575 C20.74,32.4713896 17,29.1089918 17,24.9945504 C17,21.6321526 19.6565,19 23.05,19 C24.964,19 26.801,19.8828338 28,21.2724796 C29.199,19.8828338 31.036,19 32.95,19 C36.3435,19 39,21.6321526 39,24.9945504 C39,29.1089918 35.26,32.4713896 29.595,37.5667575 L28,39 L28,39 Z
+ M18 37 L38 37 L38 37 L18 37 Z
+ M0 0 L56 0 L56 56 L0 56 Z
+
+ M2,28a26,26 0 1,0 52,0a26,26 0 1,0 -52,0
+
+ M28 2 a26 26 0 1,1 0 52
+
+ @android:integer/config_mediumAnimTime
+ 1500
+ 1400
+ @android:integer/config_mediumAnimTime
+ 1500
+ @android:integer/config_mediumAnimTime
+ 1
+ 1401
+ @android:integer/config_mediumAnimTime
+
+
+
+ M16.05,5 C14.484,5 12.981,5.70626703 12,6.81798365 C11.019,5.70626703 9.516,5 7.95,5 C5.1735,5 3,7.10572207 3,9.79564033 C3,13.0871935 6.06,15.7771117 10.695,19.853406 L12,21 L13.305,19.853406 C17.94,15.7771117 21,13.0871935 21,9.79564033 C21,7.10572207 18.8265,5 16.05,5 L16.05,5 Z M12.0945,18.5629428 L12,18.6457766 L11.9055,18.5629428 C7.626,14.800545 4.8,12.3155313 4.8,9.79564033 C4.8,8.05613079 6.1545,6.74386921 7.95,6.74386921 C9.336,6.74386921 10.686,7.61144414 11.1585,8.80163488 L12.837,8.80163488 C13.314,7.61144414 14.664,6.74386921 16.05,6.74386921 C17.8455,6.74386921 19.2,8.05613079 19.2,9.79564033 C19.2,12.3155313 16.374,14.800545 12.0945,18.5629428 L12.0945,18.5629428 Z
+ M12,21 L10.695,19.853406 C6.06,15.7771117 3,13.0871935 3,9.79564033 C3,7.10572207 5.1735,5 7.95,5 C9.516,5 11.019,5.70626703 12,6.81798365 C12.981,5.70626703 14.484,5 16.05,5 C18.8265,5 21,7.10572207 21,9.79564033 C21,13.0871935 17.94,15.7771117 13.305,19.853406 L12,21 L12,21 Z
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/integers.xml b/app/src/main/res/values/integers.xml
new file mode 100644
index 000000000..030f53c6a
--- /dev/null
+++ b/app/src/main/res/values/integers.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ 120
+ 368
+
\ No newline at end of file
diff --git a/app/src/main/res/values/no_connection.xml b/app/src/main/res/values/no_connection.xml
new file mode 100644
index 000000000..77586589b
--- /dev/null
+++ b/app/src/main/res/values/no_connection.xml
@@ -0,0 +1,23 @@
+
+
+
+
+ M26,159 L26,159 L32,159 L32,159 Z
+ M26,67 L26,159 L32,159 L32,67 Z
+ M24,206.5 C24,206.5 27.6392462,206.449997 28,206.449997 C28.3607538,206.449997 32,206.5 32,206.5
+ M23.6061137,207.020699 C23.6061137,207.020699 26.2397148,206 28.494994,206 C30.7502732,206 32.753258,207.020699 32.753258,207.020699
+
diff --git a/app/src/main/res/values/paths.xml b/app/src/main/res/values/paths.xml
new file mode 100644
index 000000000..b86f86502
--- /dev/null
+++ b/app/src/main/res/values/paths.xml
@@ -0,0 +1,23 @@
+
+
+
+
+ M44,8c0-2.2-1.8-4-4-4H8C5.8,4,4,5.8,4,8v24c0,2.2,1.8,4,4,4h28l8,8L44,8z M36,28H12v-4h24V28z M36,22H12v-4h24V22z M36,16H12v-4h24V16z
+ M38,26H26v12h-4V26H10v-4h12V10h4v12h12V26z
+ M20,36h8v-4h-8V36z M6,12v4h36v-4H6z M12,26h24v-4H12V26z
+ M13.3843083,13.3956843 C11.233862,15.5399983 7.7581039,15.5381046 5.61000013,13.3900003 C3.46000004,11.2399998 3.46000004,7.76000023 5.61000013,5.61000013 C7.76000023,3.46000004 11.2400007,3.46000004 13.3900003,5.61000013 C15.54,7.76000023 15.5400009,11.2400007 13.3900003,13.3900003 C13.388104,13.3918967 13.3862067,13.3937913 13.3843083,13.3956843 C15.1427975,15.1834093 19.6826174,19.798706 19.6826172,19.7987061 L13.3843085,13.3956846 L13.3843083,13.3956843 Z
+
\ No newline at end of file
diff --git a/app/src/main/res/values/searchback.xml b/app/src/main/res/values/searchback.xml
new file mode 100644
index 000000000..26ef53df9
--- /dev/null
+++ b/app/src/main/res/values/searchback.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+ 48dp
+ 24dp
+ 48
+ 24
+ 2
+ #fff
+
+
+ M25.39,13.39 A 5.5 5.5 0 1 1 17.61 5.61 A 5.5 5.5 0 1 1 25.39 13.39
+ - 1
+ - 0
+ 250
+ 300
+
+
+ M24.7000008,12.6999998 C24.7000008,12.6999998 31.8173374,19.9066081 31.8173371,19.9066082 C32.7867437,20.7006357 34.4599991,23 37.5,23 C40.5400009,23 43,20.54 43,17.5 C43,14.46 40.5400009,12 37.5,12 C34.4599991,12 33.2173088,12 31.8173371,12 C31.8173374,12 18.8477173,12 18.8477173,12
+ - 0
+ - 0.185
+ - 0.75
+ - 1
+
+ 600
+ 450
+
+
+ M16.7017297,12.6957157 L24.7043962,4.69304955
+ M16.7107986,11.2764828 L24.7221527,19.2878361
+ - 0
+ - 1
+ 350
+ 250
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..122906bdb
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+ Plaid
+ no comments
+ Log in to
+ Woah there chief. First you\'ll need to
+ Logged in as
+
+
+ Search
+ Filter
+ About
+ Log in to Dribbble
+ Log out of Dribbble
+ Logged out of Dribbble
+ Log in to Designer News
+ Log out of Designer News
+ Logged out of Designer News
+ Add
+ Edit
+ No filters selected, press the filter button above (ࢴ)\nor swipe from the right
+
+
+ Popular Designer News
+ Recent Designer News
+ Popular Dribbbles
+ Dribbble following
+ My Dribbble likes
+ My Dribbble shots
+ Recent Dribbbles
+ Dribbble Debuts
+ Dribbble Animated
+ Material Design
+ Product Hunt
+
+
+ Search Dribbble & Designer News
+ No results for\n“%1$s”
+ Save Dribbble
+ Save Designer News
+ Save
+
+
+ Post a story to Designer News
+ Submit a link or provide a comment to start a discussion.
+ Include a url or a comment
+ Post title
+ Url
+
+ Link
+ Comment
+
+ Comment
+ Post
+ Hello world!
+ Settings
+
+
+ Username
+ Autocomplete email addresses
+ Password
+ Sign up
+ Log in
+
+
+ Upvote story
+
+
+
+
+
+ - no likes
+ - %s like
+ - %s likes
+
+
+ - no views
+ - %s view
+ - %s views
+
+ share
+
+
+
+
+ About Plaid
+
+
+ shot
+ shot_title
+ shot_background
+ story_title
+ story_title_background
+ transition_story_background
+ dribbble_login
+ transition_designer_news_login
+ new_designer_news_post
+
+
+ io.plaidapp.shareprovider
+
+
+ Slow-carb scenester Odd Future seitan, migas mlkshk High Life cardigan Wes Anderson. Scenester mumblecore normcore lumbersexual Odd Future. Authentic mustache distillery, kale chips hoodie health goth gluten-free Odd Future letterpress kitsch hella put a bird on it narwhal Wes Anderson irony. Fashion axe Thundercats farm-to-table chia Wes Anderson, try-hard hella scenester selvage. Four dollar toast drinking vinegar plaid sustainable, polaroid stumptown organic cray 90\'s taxidermy migas blog McSweeney\'s Williamsburg meditation. Typewriter kitsch Brooklyn, raw denim occupy Shoreditch twee whatever Pitchfork plaid mlkshk cray flannel. Chillwave PBRB whatever Echo Park. Swag pickled put a bird on it deep v, selvage kitsch cliche shabby chic Brooklyn whatever. Brunch mumblecore +1 chia, single-origin coffee you probably haven\'t heard of them hella distillery sustainable. Biodiesel small batch gluten-free blog put a bird on it Tumblr. Readymade keffiyeh tilde, PBR health goth skateboard leggings fashion axe. High Life tofu squid meditation, kogi street art cronut pour-over selvage. Hella readymade stumptown, tote bag Bushwick mustache Tumblr migas butcher iPhone. Vinyl tofu Intelligentsia XOXO, pork belly Helvetica ugh photo booth 8-bit four loko.
+
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 000000000..05c341e6f
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,376 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/xml/glide_disk_cache_path.xml b/app/src/main/res/xml/glide_disk_cache_path.xml
new file mode 100644
index 000000000..173065dc7
--- /dev/null
+++ b/app/src/main/res/xml/glide_disk_cache_path.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/xml/searchable.xml b/app/src/main/res/xml/searchable.xml
new file mode 100644
index 000000000..976bc2b81
--- /dev/null
+++ b/app/src/main/res/xml/searchable.xml
@@ -0,0 +1,22 @@
+
+
+
diff --git a/art/dribbble_logo.svg b/art/dribbble_logo.svg
new file mode 100644
index 000000000..521b3c342
--- /dev/null
+++ b/art/dribbble_logo.svg
@@ -0,0 +1,10 @@
+
+
+
+ Shape
+ Created with Sketch.
+
+
+
+
+
\ No newline at end of file
diff --git a/art/ic_add_comment.svg b/art/ic_add_comment.svg
new file mode 100644
index 000000000..0d3892c0c
--- /dev/null
+++ b/art/ic_add_comment.svg
@@ -0,0 +1,15 @@
+
+
+
+ mode_comment_
+ Created with Sketch.
+
+
+
+
+
\ No newline at end of file
diff --git a/art/ic_comment.svg b/art/ic_comment.svg
new file mode 100644
index 000000000..991cd9951
--- /dev/null
+++ b/art/ic_comment.svg
@@ -0,0 +1,15 @@
+
+
+
+ mode_comment_
+ Created with Sketch.
+
+
+
+
+
\ No newline at end of file
diff --git a/art/ic_designer_news.sketch b/art/ic_designer_news.sketch
new file mode 100644
index 000000000..2c19b8426
Binary files /dev/null and b/art/ic_designer_news.sketch differ
diff --git a/art/ic_designer_news.svg b/art/ic_designer_news.svg
new file mode 100644
index 000000000..30704a380
--- /dev/null
+++ b/art/ic_designer_news.svg
@@ -0,0 +1,20 @@
+
+
+
+ ic_designer_news
+ Created with Sketch.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/art/ic_dribbble.sketch b/art/ic_dribbble.sketch
new file mode 100644
index 000000000..c6d1521b3
Binary files /dev/null and b/art/ic_dribbble.sketch differ
diff --git a/art/ic_dribbble.svg b/art/ic_dribbble.svg
new file mode 100644
index 000000000..6d4d48f76
--- /dev/null
+++ b/art/ic_dribbble.svg
@@ -0,0 +1,16 @@
+
+
+
+ ic_dribbble
+ Created with Sketch.
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/art/ic_no_comments.sketch b/art/ic_no_comments.sketch
new file mode 100644
index 000000000..f7aba1bf7
Binary files /dev/null and b/art/ic_no_comments.sketch differ
diff --git a/art/ic_no_comments.svg b/art/ic_no_comments.svg
new file mode 100644
index 000000000..72dc242a4
--- /dev/null
+++ b/art/ic_no_comments.svg
@@ -0,0 +1,13 @@
+
+
+
+ ic_no_comments
+ Created with Sketch.
+
+
+
+
+
\ No newline at end of file
diff --git a/art/ic_player.sketch b/art/ic_player.sketch
new file mode 100644
index 000000000..3b282bbb6
Binary files /dev/null and b/art/ic_player.sketch differ
diff --git a/art/ic_player.svg b/art/ic_player.svg
new file mode 100644
index 000000000..ca2f51fb5
--- /dev/null
+++ b/art/ic_player.svg
@@ -0,0 +1,18 @@
+
+
+
+ ic_player
+ Created with Sketch.
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/art/ic_pocket.sketch/Data b/art/ic_pocket.sketch/Data
new file mode 100644
index 000000000..b9f9da9cc
Binary files /dev/null and b/art/ic_pocket.sketch/Data differ
diff --git a/art/ic_pocket.sketch/metadata b/art/ic_pocket.sketch/metadata
new file mode 100644
index 000000000..77532ce97
--- /dev/null
+++ b/art/ic_pocket.sketch/metadata
@@ -0,0 +1,18 @@
+
+
+
+
+ app
+ com.bohemiancoding.sketch3
+ build
+ 7891
+ commit
+ debc570766a4cc5a2e31258967910f7e5776f485
+ fonts
+
+ length
+ 14930
+ version
+ 37
+
+
diff --git a/art/ic_pocket.sketch/version b/art/ic_pocket.sketch/version
new file mode 100644
index 000000000..7c091989d
--- /dev/null
+++ b/art/ic_pocket.sketch/version
@@ -0,0 +1 @@
+37
\ No newline at end of file
diff --git a/art/ic_pocket.svg b/art/ic_pocket.svg
new file mode 100644
index 000000000..eda5ba71c
--- /dev/null
+++ b/art/ic_pocket.svg
@@ -0,0 +1,12 @@
+
+
+
+ ic_pocket
+ Created with Sketch.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/art/ic_product_hunt.svg b/art/ic_product_hunt.svg
new file mode 100644
index 000000000..335196e93
--- /dev/null
+++ b/art/ic_product_hunt.svg
@@ -0,0 +1,14 @@
+
+
+
+ Imported Layers
+ Created with Sketch Beta.
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/art/ic_search.svg b/art/ic_search.svg
new file mode 100644
index 000000000..33ab25072
--- /dev/null
+++ b/art/ic_search.svg
@@ -0,0 +1,10 @@
+
+
+
+ ic_search
+ Created with Sketch.
+
+
+
+
+
\ No newline at end of file
diff --git a/art/no_connection.sketch b/art/no_connection.sketch
new file mode 100644
index 000000000..5d1aa5370
Binary files /dev/null and b/art/no_connection.sketch differ
diff --git a/art/no_connection.svg b/art/no_connection.svg
new file mode 100644
index 000000000..b509a89e6
--- /dev/null
+++ b/art/no_connection.svg
@@ -0,0 +1,25 @@
+
+
+
+ no_connection
+ Created with Sketch.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 000000000..e678484cf
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,13 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.0.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 000000000..46997f128
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,30 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Settings specified in this file will override any Gradle settings
+# configured through the IDE.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+
+# Dribbble API
+dribbble_client_id =
+dribbble_client_secret =
+dribbble_client_access_token =
+
+# Design News API
+designer_news_client_id =
+designer_news_client_secret =
+
+# Product Hunt API
+product_hunt_developer_token =
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..8c0fb64a8
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..11f1e068e
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 01 15:49:30 GMT 2014
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 000000000..91a7e269e
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 000000000..aec99730b
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/screenshots/dn_story_framed.png b/screenshots/dn_story_framed.png
new file mode 100644
index 000000000..e9d998ca1
Binary files /dev/null and b/screenshots/dn_story_framed.png differ
diff --git a/screenshots/dribbble_shot_framed.png b/screenshots/dribbble_shot_framed.png
new file mode 100644
index 000000000..2ea50cb3b
Binary files /dev/null and b/screenshots/dribbble_shot_framed.png differ
diff --git a/screenshots/home_grid_framed.png b/screenshots/home_grid_framed.png
new file mode 100644
index 000000000..812198926
Binary files /dev/null and b/screenshots/home_grid_framed.png differ
diff --git a/screenshots/plaid_demo.gif b/screenshots/plaid_demo.gif
new file mode 100644
index 000000000..dd4dbf51e
Binary files /dev/null and b/screenshots/plaid_demo.gif differ
diff --git a/screenshots/post_story_framed.png b/screenshots/post_story_framed.png
new file mode 100644
index 000000000..d3d4535b0
Binary files /dev/null and b/screenshots/post_story_framed.png differ
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 000000000..5b9481f2f
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,2 @@
+include ':app', ':bypass'
+project(':bypass').projectDir = new File(rootDir, 'third_party/bypass')
diff --git a/third_party/bypass/LICENSE.txt b/third_party/bypass/LICENSE.txt
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/third_party/bypass/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/third_party/bypass/README.google b/third_party/bypass/README.google
new file mode 100644
index 000000000..5843dc47b
--- /dev/null
+++ b/third_party/bypass/README.google
@@ -0,0 +1,11 @@
+URL: https://github.com/Uncodin/bypass/tree/b0e33f9b9dce33a128ab6e9ab895ffbea518c878
+Version: 1.1
+License: Apache License v2.0
+License File: LICENSE.txt
+
+Description:
+Fork of https://github.com/Uncodin/bypass
+
+Local Modifications:
+Added TouchableUrlSpan, FancyQuoteSpan & ImageLoadingSpan + LoadImageCallback
+mechanism.
diff --git a/third_party/bypass/README.md b/third_party/bypass/README.md
new file mode 100644
index 000000000..6c23dffbb
--- /dev/null
+++ b/third_party/bypass/README.md
@@ -0,0 +1,14 @@
+# Bypass
+
+Fork of the awesome markdown processor [Bypass](https://github.com/Uncodin/bypass).
+
+## Modifications
+
+Augmented to add:
+
+- `TouchableUrlSpan` An extension to URLSpan which changes it's background & foreground color when
+pressed.
+- `FancyQuoteSpan` A quote span with a nicer presentation
+- `ImageLoadingSpan` A simple text span used to mark text that will be replaced by an image once it
+has been downloaded
+- `LoadImageCallback` a callback mechanism for loading images
diff --git a/third_party/bypass/build.gradle b/third_party/bypass/build.gradle
new file mode 100644
index 000000000..95cb1acd3
--- /dev/null
+++ b/third_party/bypass/build.gradle
@@ -0,0 +1,23 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "23.0.1"
+
+ defaultConfig {
+ minSdkVersion 8
+ targetSdkVersion 16
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ compile 'com.android.support:support-annotations:23.1.0'
+}
diff --git a/third_party/bypass/libs/bypass-native-libs.jar b/third_party/bypass/libs/bypass-native-libs.jar
new file mode 100644
index 000000000..120fc2f50
Binary files /dev/null and b/third_party/bypass/libs/bypass-native-libs.jar differ
diff --git a/third_party/bypass/src/main/AndroidManifest.xml b/third_party/bypass/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..160041d7f
--- /dev/null
+++ b/third_party/bypass/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
diff --git a/third_party/bypass/src/main/java/in/uncod/android/bypass/Bypass.java b/third_party/bypass/src/main/java/in/uncod/android/bypass/Bypass.java
new file mode 100755
index 000000000..df64196db
--- /dev/null
+++ b/third_party/bypass/src/main/java/in/uncod/android/bypass/Bypass.java
@@ -0,0 +1,435 @@
+package in.uncod.android.bypass;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.support.annotation.ColorInt;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.text.style.AbsoluteSizeSpan;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.LeadingMarginSpan;
+import android.text.style.RelativeSizeSpan;
+import android.text.style.StrikethroughSpan;
+import android.text.style.StyleSpan;
+import android.text.style.TypefaceSpan;
+import android.util.DisplayMetrics;
+import android.util.Patterns;
+import android.util.TypedValue;
+import android.widget.TextView;
+
+import in.uncod.android.bypass.style.FancyQuoteSpan;
+import in.uncod.android.bypass.style.ImageLoadingSpan;
+
+import in.uncod.android.bypass.Element.Type;
+import in.uncod.android.bypass.style.HorizontalLineSpan;
+import in.uncod.android.bypass.style.TouchableUrlSpan;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class Bypass {
+ static {
+ System.loadLibrary("bypass");
+ }
+
+ private final Options mOptions;
+
+ private final int mListItemIndent;
+ private final int mBlockQuoteIndent;
+ private final int mBlockQuoteLineWidth;
+ private final int mBlockQuoteLineIndent;
+ private final int mCodeBlockIndent;
+ private final int mHruleSize;
+
+ private final int mHruleTopBottomPadding;
+
+ // Keeps track of the ordered list number for each LIST element.
+ // We need to track multiple ordered lists at once because of nesting.
+ private final Map mOrderedListNumber = new ConcurrentHashMap();
+
+ public Bypass(Context context, Options options) {
+ mOptions = options;
+
+ DisplayMetrics dm = context.getResources().getDisplayMetrics();
+
+ mListItemIndent = (int) TypedValue.applyDimension(mOptions.mListItemIndentUnit,
+ mOptions.mListItemIndentSize, dm);
+
+ mBlockQuoteIndent = (int) TypedValue.applyDimension(mOptions.mBlockQuoteIndentUnit,
+ mOptions.mBlockQuoteIndentSize, dm);
+
+ mBlockQuoteLineWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ mOptions.mBlockQuoteLineWidth, dm);
+
+ mBlockQuoteLineIndent = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ mOptions.mBlockQuoteLineIndent, dm);
+
+ mCodeBlockIndent = (int) TypedValue.applyDimension(mOptions.mCodeBlockIndentUnit,
+ mOptions.mCodeBlockIndentSize, dm);
+
+ mHruleSize = (int) TypedValue.applyDimension(mOptions.mHruleUnit,
+ mOptions.mHruleSize, dm);
+
+ mHruleTopBottomPadding = (int) dm.density * 10;
+ }
+
+ private static void setSpan(SpannableStringBuilder builder, Object what) {
+ builder.setSpan(what, 0, builder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ // These have trailing newlines that we want to avoid spanning
+ private static void setBlockSpan(SpannableStringBuilder builder, Object what) {
+ int length = Math.max(0, builder.length() - 1);
+ builder.setSpan(what, 0, length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ private static void setSpanWithPrependedNewline(SpannableStringBuilder builder, Object what) {
+ builder.setSpan(what, 1, builder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ private static void setPrependedNewlineSpan(SpannableStringBuilder builder, int height) {
+ builder.setSpan(new AbsoluteSizeSpan(height, true), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ public CharSequence markdownToSpannable(String markdown, TextView textView, LoadImageCallback loadImageCallback) {
+ Document document = processMarkdown(markdown);
+
+ int size = document.getElementCount();
+ CharSequence[] spans = new CharSequence[size];
+
+ for (int i = 0; i < size; i++) {
+ spans[i] = recurseElement(document.getElement(i), i, size, textView, loadImageCallback);
+ }
+
+ return TextUtils.concat(spans);
+ }
+
+ private native Document processMarkdown(String markdown);
+
+ // The 'numberOfSiblings' parameters refers to the number of siblings within the parent, including
+ // the 'element' parameter, as in "How many siblings are you?" rather than "How many siblings do
+ // you have?".
+ private CharSequence recurseElement(Element element, int indexWithinParent, int numberOfSiblings,
+ TextView textView, LoadImageCallback loadImageCallback) {
+
+ Type type = element.getType();
+
+ boolean isOrderedList = false;
+ if (type == Type.LIST) {
+ String flagsStr = element.getAttribute("flags");
+ if (flagsStr != null) {
+ int flags = Integer.parseInt(flagsStr);
+ isOrderedList = (flags & Element.F_LIST_ORDERED) != 0;
+ if (isOrderedList) {
+ mOrderedListNumber.put(element, 1);
+ }
+ }
+ }
+
+ int size = element.size();
+ CharSequence[] spans = new CharSequence[size];
+
+ for (int i = 0; i < size; i++) {
+ spans[i] = recurseElement(element.children[i], i, size, textView, loadImageCallback);
+ }
+
+ // Clean up after we're done
+ if (isOrderedList) {
+ mOrderedListNumber.remove(this);
+ }
+
+ CharSequence concat = TextUtils.concat(spans);
+
+ SpannableStringBuilder builder = new ReverseSpannableStringBuilder();
+
+ String text = element.getText();
+ if (element.size() == 0
+ && element.getParent() != null
+ && element.getParent().getType() != Type.BLOCK_CODE) {
+ text = text.replace('\n', ' ');
+ }
+
+ switch (type) {
+ case LIST:
+ if (element.getParent() != null
+ && element.getParent().getType() == Type.LIST_ITEM) {
+ builder.append("\n");
+ }
+ break;
+ case LINEBREAK:
+ builder.append("\n");
+ break;
+ case LIST_ITEM:
+ builder.append(" ");
+ if (mOrderedListNumber.containsKey(element.getParent())) {
+ int number = mOrderedListNumber.get(element.getParent());
+ builder.append(Integer.toString(number) + ".");
+ mOrderedListNumber.put(element.getParent(), number + 1);
+ } else {
+ builder.append(mOptions.mUnorderedListItem);
+ }
+ builder.append(" ");
+ break;
+ case AUTOLINK:
+ builder.append(element.getAttribute("link"));
+ break;
+ case HRULE:
+ // This ultimately gets drawn over by the line span, but
+ // we need something here or the span isn't even drawn.
+ builder.append("-");
+ break;
+ case IMAGE:
+ if (loadImageCallback != null && !TextUtils.isEmpty(element.getAttribute("link"))) {
+ // prepend a new line so that images are always on a new line
+ builder.append("\n");
+ // Display alt text (or title text) if there is no image
+ String alt = element.getAttribute("alt");
+ if (TextUtils.isEmpty(alt)) {
+ alt = element.getAttribute("title");
+ }
+ if (!TextUtils.isEmpty(alt)) {
+ alt = "[" + alt + "]";
+ builder.append(alt);
+ } else {
+ // Character to be replaced
+ builder.append("\uFFFC");
+ }
+ } else {
+ // Character to be replaced
+ builder.append("\uFFFC");
+ }
+ break;
+ }
+
+ builder.append(text);
+ builder.append(concat);
+
+ // Don't auto-append whitespace after last item in document. The 'numberOfSiblings'
+ // is the number of children the parent of the current element has (including the
+ // element itself), hence subtracting a number from that count gives us the index
+ // of the last child within the parent.
+ if (element.getParent() != null || indexWithinParent < (numberOfSiblings - 1)) {
+ if (type == Type.LIST_ITEM) {
+ if (element.size() == 0 || !element.children[element.size() - 1].isBlockElement()) {
+ builder.append("\n");
+ }
+ } else if (element.isBlockElement() && type != Type.BLOCK_QUOTE) {
+ if (type == Type.LIST) {
+ // If this is a nested list, don't include newlines
+ if (element.getParent() == null || element.getParent().getType() != Type.LIST_ITEM) {
+ builder.append("\n");
+ }
+ } else if (element.getParent() != null
+ && element.getParent().getType() == Type.LIST_ITEM) {
+ // List items should never double-space their entries
+ builder.append("\n");
+ } else {
+ builder.append("\n\n");
+ }
+ }
+ }
+
+ switch (type) {
+ case HEADER:
+ String levelStr = element.getAttribute("level");
+ int level = Integer.parseInt(levelStr);
+ setSpan(builder, new RelativeSizeSpan(mOptions.mHeaderSizes[level - 1]));
+ setSpan(builder, new StyleSpan(Typeface.BOLD));
+ break;
+ case LIST:
+ setBlockSpan(builder, new LeadingMarginSpan.Standard(mListItemIndent));
+ break;
+ case EMPHASIS:
+ setSpan(builder, new StyleSpan(Typeface.ITALIC));
+ break;
+ case DOUBLE_EMPHASIS:
+ setSpan(builder, new StyleSpan(Typeface.BOLD));
+ break;
+ case TRIPLE_EMPHASIS:
+ setSpan(builder, new StyleSpan(Typeface.BOLD_ITALIC));
+ break;
+ case BLOCK_CODE:
+ setSpan(builder, new LeadingMarginSpan.Standard(mCodeBlockIndent));
+ setSpan(builder, new TypefaceSpan("monospace"));
+ break;
+ case CODE_SPAN:
+ setSpan(builder, new TypefaceSpan("monospace"));
+ break;
+ case LINK:
+ case AUTOLINK:
+ String link = element.getAttribute("link");
+ if (!TextUtils.isEmpty(link) && Patterns.EMAIL_ADDRESS.matcher(link).matches()) {
+ link = "mailto:" + link;
+ }
+ setSpan(builder, new TouchableUrlSpan(link, textView.getLinkTextColors(),
+ textView.getHighlightColor()));
+ break;
+ case BLOCK_QUOTE:
+ // We add two leading margin spans so that when the order is reversed,
+ // the QuoteSpan will always be in the same spot.
+ setBlockSpan(builder, new LeadingMarginSpan.Standard(mBlockQuoteIndent));
+ //setBlockSpan(builder, new QuoteSpan(mOptions.mBlockQuoteLineColor));
+ setBlockSpan(builder, new FancyQuoteSpan(mBlockQuoteLineWidth, mBlockQuoteLineIndent, mOptions.mBlockQuoteLineColor));
+ setBlockSpan(builder, new ForegroundColorSpan(mOptions.mBlockQuoteTextColor));
+ setBlockSpan(builder, new LeadingMarginSpan.Standard(mBlockQuoteIndent));
+ setBlockSpan(builder, new StyleSpan(Typeface.ITALIC));
+ break;
+ case STRIKETHROUGH:
+ setSpan(builder, new StrikethroughSpan());
+ break;
+ case HRULE:
+ setSpan(builder, new HorizontalLineSpan(mOptions.mHruleColor, mHruleSize, mHruleTopBottomPadding));
+ break;
+ case IMAGE:
+ String url = element.getAttribute("link");
+ if (loadImageCallback != null && !TextUtils.isEmpty(url)) {
+ setPrependedNewlineSpan(builder, mOptions.mPreImageLinebreakHeight);
+ ImageLoadingSpan loadingSpan = new ImageLoadingSpan();
+ setSpanWithPrependedNewline(builder, loadingSpan);
+ // make the (eventually loaded) image span clickable to open in browser
+ setSpanWithPrependedNewline(builder, new TouchableUrlSpan(url, textView.getLinkTextColors(), textView.getHighlightColor()));
+ loadImageCallback.loadImage(url, loadingSpan);
+ }
+ break;
+ }
+
+ return builder;
+ }
+
+ public interface LoadImageCallback {
+ /**
+ * A callback to load an image found in a markdown document.
+ * @param src The source (url) of the image.
+ * @param loadingSpan A placeholder span making where the image should be inserted.
+ */
+ void loadImage(String src, ImageLoadingSpan loadingSpan);
+ }
+
+ /**
+ * Configurable options for how Bypass renders certain elements.
+ */
+ public static final class Options {
+ private float[] mHeaderSizes;
+
+ private String mUnorderedListItem;
+ private int mListItemIndentUnit;
+ private float mListItemIndentSize;
+
+ private int mBlockQuoteLineColor;
+ private int mBlockQuoteTextColor;
+ private int mBlockQuoteLineWidth;
+ private int mBlockQuoteLineIndent;
+ private int mBlockQuoteIndentUnit;
+ private float mBlockQuoteIndentSize;
+
+ private int mPreImageLinebreakHeight;
+
+ private int mCodeBlockIndentUnit;
+ private float mCodeBlockIndentSize;
+
+ private int mHruleColor;
+ private int mHruleUnit;
+ private float mHruleSize;
+
+ public Options() {
+ mHeaderSizes = new float[]{
+ 1.5f, // h1
+ 1.4f, // h2
+ 1.3f, // h3
+ 1.2f, // h4
+ 1.1f, // h5
+ 1.0f, // h6
+ };
+
+ mUnorderedListItem = "\u2022";
+ mListItemIndentUnit = TypedValue.COMPLEX_UNIT_DIP;
+ mListItemIndentSize = 10;
+
+ mBlockQuoteLineColor = 0xff0000ff;
+ mBlockQuoteIndentUnit = TypedValue.COMPLEX_UNIT_DIP;
+ mBlockQuoteIndentSize = 10;
+
+ mPreImageLinebreakHeight = 4;
+
+ mCodeBlockIndentUnit = TypedValue.COMPLEX_UNIT_DIP;
+ mCodeBlockIndentSize = 10;
+
+ mHruleColor = Color.GRAY;
+ mHruleUnit = TypedValue.COMPLEX_UNIT_DIP;
+ mHruleSize = 1;
+ }
+
+ public Options setHeaderSizes(float[] headerSizes) {
+ if (headerSizes == null) {
+ throw new IllegalArgumentException("headerSizes must not be null");
+ } else if (headerSizes.length != 6) {
+ throw new IllegalArgumentException("headerSizes must have 6 elements (h1 through h6)");
+ }
+
+ mHeaderSizes = headerSizes;
+
+ return this;
+ }
+
+ public Options setUnorderedListItem(String unorderedListItem) {
+ mUnorderedListItem = unorderedListItem;
+ return this;
+ }
+
+ public Options setListItemIndentSize(int unit, float size) {
+ mListItemIndentUnit = unit;
+ mListItemIndentSize = size;
+ return this;
+ }
+
+ public Options setBlockQuoteLineColor(@ColorInt int color) {
+ mBlockQuoteLineColor = color;
+ return this;
+ }
+
+ public Options setBlockQuoteTextColor(@ColorInt int color) {
+ mBlockQuoteTextColor = color;
+ return this;
+ }
+
+ public Options setBlockQuoteLineWidth(int width) {
+ mBlockQuoteLineWidth = width;
+ return this;
+ }
+
+ public Options setBlockQuoteLineIndent(int indent) {
+ mBlockQuoteLineIndent = indent;
+ return this;
+ }
+
+ public Options setBlockQuoteIndentSize(int unit, float size) {
+ mBlockQuoteIndentUnit = unit;
+ mBlockQuoteIndentSize = size;
+ return this;
+ }
+
+ public Options setPreImageLinebreakHeight(int height) {
+ mPreImageLinebreakHeight = height;
+ return this;
+ }
+
+ public Options setCodeBlockIndentSize(int unit, float size) {
+ mCodeBlockIndentUnit = unit;
+ mCodeBlockIndentSize = size;
+ return this;
+ }
+
+ public Options setHruleColor(@ColorInt int color) {
+ mHruleColor = color;
+ return this;
+ }
+
+ public Options setHruleSize(int unit, float size) {
+ mHruleUnit = unit;
+ mHruleSize = size;
+ return this;
+ }
+ }
+}
diff --git a/third_party/bypass/src/main/java/in/uncod/android/bypass/Document.java b/third_party/bypass/src/main/java/in/uncod/android/bypass/Document.java
new file mode 100755
index 000000000..998a5f5ba
--- /dev/null
+++ b/third_party/bypass/src/main/java/in/uncod/android/bypass/Document.java
@@ -0,0 +1,18 @@
+package in.uncod.android.bypass;
+
+public class Document {
+
+ Element[] elements;
+
+ public Document(Element[] elements) {
+ this.elements = elements;
+ }
+
+ public int getElementCount() {
+ return elements.length;
+ }
+
+ public Element getElement(int pos) {
+ return elements[pos];
+ }
+}
diff --git a/third_party/bypass/src/main/java/in/uncod/android/bypass/Element.java b/third_party/bypass/src/main/java/in/uncod/android/bypass/Element.java
new file mode 100755
index 000000000..38ff4caf4
--- /dev/null
+++ b/third_party/bypass/src/main/java/in/uncod/android/bypass/Element.java
@@ -0,0 +1,109 @@
+package in.uncod.android.bypass;
+
+import java.util.Map;
+import java.util.HashMap;
+
+public class Element {
+
+ public static final int F_LIST_ORDERED = 1;
+ String text;
+ Map attributes = new HashMap();
+ Element[] children;
+ Type type;
+ Element parent;
+ int nestLevel = 0;
+ public Element(String text, int type) {
+ this.text = text;
+ this.type = Type.fromInteger(type);
+ }
+
+ public void setChildren(Element[] children) {
+ this.children = children;
+ }
+
+ public void addAttribute(String name, String value) {
+ attributes.put(name, value);
+ }
+
+ public String getAttribute(String name) {
+ return attributes.get(name);
+ }
+
+ public Element getParent() {
+ return parent;
+ }
+
+ public void setParent(Element element) {
+ this.parent = element;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public int size() {
+ if (children != null) {
+ return children.length;
+ }
+ return 0;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public boolean isBlockElement() {
+ return (type.value & 0x100) == 0x000;
+ }
+
+ public boolean isSpanElement() {
+ return (type.value & 0x100) == 0x100;
+ }
+
+ public enum Type {
+
+ // Block Element Types
+
+ BLOCK_CODE(0x000),
+ BLOCK_QUOTE(0x001),
+ BLOCK_HTML(0x002),
+ HEADER(0x003),
+ HRULE(0x004),
+ LIST(0x005),
+ LIST_ITEM(0x006),
+ PARAGRAPH(0x007),
+ TABLE(0x008),
+ TABLE_CELL(0x009),
+ TABLE_ROW(0x00A),
+
+ // Span Element Types
+
+ AUTOLINK(0x10B),
+ CODE_SPAN(0x10C),
+ DOUBLE_EMPHASIS(0x10D),
+ EMPHASIS(0x10E),
+ IMAGE(0x10F),
+ LINEBREAK(0x110),
+ LINK(0x111),
+ RAW_HTML_TAG(0x112),
+ TRIPLE_EMPHASIS(0x113),
+ TEXT(0x114),
+ STRIKETHROUGH(0x115);
+
+ private static final Type[] TypeValues = Type.values();
+ private final int value;
+
+ private Type(int value) {
+ this.value = value;
+ }
+
+ public static Type fromInteger(int x) {
+ for (Type type : TypeValues) {
+ if (type.value == x) {
+ return type;
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/third_party/bypass/src/main/java/in/uncod/android/bypass/ReverseSpannableStringBuilder.java b/third_party/bypass/src/main/java/in/uncod/android/bypass/ReverseSpannableStringBuilder.java
new file mode 100755
index 000000000..f524f3fa6
--- /dev/null
+++ b/third_party/bypass/src/main/java/in/uncod/android/bypass/ReverseSpannableStringBuilder.java
@@ -0,0 +1,43 @@
+package in.uncod.android.bypass;
+
+import android.text.SpannableStringBuilder;
+
+/**
+ * Exactly the same as SpannableStringBuilder, but it returns its spans in reverse.
+ *
+ * What effect does this have? Well, if you're building up a Spannable recursively (as we
+ * are doing in Bypass) then returning the spans in reverse order has the correct effect
+ * in some corner cases regarding leading spans.
+ *
+ * Example:
+ * Suppose we have a BLOCK_QUOTE with a LIST inside of it. Both of them have leading spans, but the LIST
+ * span is set first. As a result, the QuoteSpan for the BLOCK_QUOTE is actually indented by the LIST's span!
+ * If the order is reversed, then the LIST's margin span is properly indented (and the BlockQuote remains on
+ * the side).
+ */
+public class ReverseSpannableStringBuilder extends SpannableStringBuilder {
+
+ private static void reverse(Object[] arr) {
+ if (arr == null) {
+ return;
+ }
+
+ int i = 0;
+ int j = arr.length - 1;
+ Object tmp;
+ while (j > i) {
+ tmp = arr[j];
+ arr[j] = arr[i];
+ arr[i] = tmp;
+ j--;
+ i++;
+ }
+ }
+
+ @Override
+ public T[] getSpans(int queryStart, int queryEnd, Class kind) {
+ T[] ret = super.getSpans(queryStart, queryEnd, kind);
+ reverse(ret);
+ return ret;
+ }
+}
diff --git a/third_party/bypass/src/main/java/in/uncod/android/bypass/style/FancyQuoteSpan.java b/third_party/bypass/src/main/java/in/uncod/android/bypass/style/FancyQuoteSpan.java
new file mode 100644
index 000000000..cbd14f643
--- /dev/null
+++ b/third_party/bypass/src/main/java/in/uncod/android/bypass/style/FancyQuoteSpan.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package in.uncod.android.bypass.style;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.support.annotation.ColorInt;
+import android.text.Layout;
+import android.text.style.LeadingMarginSpan;
+
+/**
+ * A quote span with a nicer presentation
+ */
+public class FancyQuoteSpan implements LeadingMarginSpan {
+
+ private final int lineColor;
+ private final int lineWidth;
+ private final int gapWidth;
+
+ public FancyQuoteSpan(int quoteLineWidth,
+ int quoteLineIndent,
+ @ColorInt int quoteLineColor) {
+ super();
+ lineWidth = quoteLineWidth;
+ gapWidth = quoteLineIndent;
+ lineColor = quoteLineColor;
+ }
+
+ public int getLeadingMargin(boolean first) {
+ return lineWidth + gapWidth;
+ }
+
+ public void drawLeadingMargin(Canvas c,
+ Paint p,
+ int x,
+ int dir,
+ int top,
+ int baseline,
+ int bottom,
+ CharSequence text,
+ int start,
+ int end,
+ boolean first,
+ Layout layout) {
+ Paint.Style prevStyle = p.getStyle();
+ int prevColor = p.getColor();
+ p.setStyle(Paint.Style.FILL);
+ p.setColor(lineColor);
+ c.drawRect(x, top, x + dir * lineWidth, bottom, p);
+ p.setStyle(prevStyle);
+ p.setColor(prevColor);
+ }
+}
diff --git a/third_party/bypass/src/main/java/in/uncod/android/bypass/style/HorizontalLineSpan.java b/third_party/bypass/src/main/java/in/uncod/android/bypass/style/HorizontalLineSpan.java
new file mode 100755
index 000000000..91297bb34
--- /dev/null
+++ b/third_party/bypass/src/main/java/in/uncod/android/bypass/style/HorizontalLineSpan.java
@@ -0,0 +1,45 @@
+package in.uncod.android.bypass.style;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.text.style.ReplacementSpan;
+
+/**
+ * Draws a line across the screen.
+ */
+public class HorizontalLineSpan extends ReplacementSpan {
+
+ private Paint mPaint;
+ private int mLineHeight;
+ private int mTopBottomPadding;
+
+ public HorizontalLineSpan(int color, int lineHeight, int topBottomPadding) {
+ mPaint = new Paint();
+ mPaint.setColor(color);
+ mLineHeight = lineHeight;
+ mTopBottomPadding = topBottomPadding;
+ }
+
+ @Override
+ public int getSize(
+ Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
+ if (fm != null) {
+ fm.ascent = -mLineHeight - mTopBottomPadding;
+ fm.descent = 0;
+
+ fm.top = fm.ascent;
+ fm.bottom = 0;
+ }
+
+ // Take up *all* the horizontal space
+ return Integer.MAX_VALUE;
+ }
+
+ @Override
+ public void draw(
+ Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
+ int middle = (top + bottom) / 2;
+ int halfLineHeight = mLineHeight / 2;
+ canvas.drawRect(x, middle - halfLineHeight, Integer.MAX_VALUE, middle + halfLineHeight, mPaint);
+ }
+}
diff --git a/third_party/bypass/src/main/java/in/uncod/android/bypass/style/ImageLoadingSpan.java b/third_party/bypass/src/main/java/in/uncod/android/bypass/style/ImageLoadingSpan.java
new file mode 100644
index 000000000..7b7e6b440
--- /dev/null
+++ b/third_party/bypass/src/main/java/in/uncod/android/bypass/style/ImageLoadingSpan.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package in.uncod.android.bypass.style;
+
+import android.text.TextPaint;
+import android.text.style.CharacterStyle;
+
+/**
+ * A simple text span used to mark text that will be replaced by an image once it has been
+ * downloaded. See {@link in.uncod.android.bypass.Bypass.LoadImageCallback}
+ */
+public class ImageLoadingSpan extends CharacterStyle {
+ @Override
+ public void updateDrawState(TextPaint textPaint) {
+ // no-op
+ }
+}
diff --git a/third_party/bypass/src/main/java/in/uncod/android/bypass/style/TouchableUrlSpan.java b/third_party/bypass/src/main/java/in/uncod/android/bypass/style/TouchableUrlSpan.java
new file mode 100644
index 000000000..92a513cf6
--- /dev/null
+++ b/third_party/bypass/src/main/java/in/uncod/android/bypass/style/TouchableUrlSpan.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package in.uncod.android.bypass.style;
+
+import android.content.res.ColorStateList;
+import android.text.TextPaint;
+import android.text.style.URLSpan;
+
+/**
+ * An extension to URLSpan which changes it's background & foreground color when clicked.
+ *
+ * Derived from http://stackoverflow.com/a/20905824
+ */
+public class TouchableUrlSpan extends URLSpan {
+
+ private static int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
+ private boolean isPressed;
+ private int normalTextColor;
+ private int pressedTextColor;
+ private int pressedBackgroundColor;
+
+ public TouchableUrlSpan(String url,
+ ColorStateList textColor,
+ int pressedBackgroundColor) {
+ super(url);
+ this.normalTextColor = textColor.getDefaultColor();
+ this.pressedTextColor = textColor.getColorForState(STATE_PRESSED, normalTextColor);
+ this.pressedBackgroundColor = pressedBackgroundColor;
+ }
+
+ public void setPressed(boolean isPressed) {
+ this.isPressed = isPressed;
+ }
+
+ @Override
+ public void updateDrawState(TextPaint drawState) {
+ drawState.setColor(isPressed ? pressedTextColor : normalTextColor);
+ drawState.bgColor = isPressed ? pressedBackgroundColor : 0;
+ drawState.setUnderlineText(!isPressed);
+ }
+}
\ No newline at end of file
diff --git a/third_party/bypass/src/main/res/values/strings.xml b/third_party/bypass/src/main/res/values/strings.xml
new file mode 100644
index 000000000..1945135ab
--- /dev/null
+++ b/third_party/bypass/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ Bypass
+