diff --git a/demos/java/io/material/demo/shrine/AndroidManifest.xml b/demos/java/io/material/demo/shrine/AndroidManifest.xml new file mode 100644 index 000000000..539b8dba5 --- /dev/null +++ b/demos/java/io/material/demo/shrine/AndroidManifest.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + diff --git a/demos/java/io/material/demo/shrine/filters/AndroidManifest.xml b/demos/java/io/material/demo/shrine/filters/AndroidManifest.xml new file mode 100644 index 000000000..748468c03 --- /dev/null +++ b/demos/java/io/material/demo/shrine/filters/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + diff --git a/demos/java/io/material/demo/shrine/filters/FiltersActivity.java b/demos/java/io/material/demo/shrine/filters/FiltersActivity.java new file mode 100644 index 000000000..52401ef9b --- /dev/null +++ b/demos/java/io/material/demo/shrine/filters/FiltersActivity.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017 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.material.demo.shrine.filters; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; + +/** + * Activity that displays a list of filters the user can set to refine the list of products. + */ +public class FiltersActivity extends AppCompatActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.shrine_filters_activity); + initToolbar(); + } + + private void initToolbar() { + Toolbar toolbar = (Toolbar) findViewById(R.id.AppBar); + setSupportActionBar(toolbar); + getSupportActionBar().setTitle(getString(R.string.shrine_filter_page_title)); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } +} diff --git a/demos/java/io/material/demo/shrine/filters/res/layout/shrine_filters_activity.xml b/demos/java/io/material/demo/shrine/filters/res/layout/shrine_filters_activity.xml new file mode 100644 index 000000000..7e245f859 --- /dev/null +++ b/demos/java/io/material/demo/shrine/filters/res/layout/shrine_filters_activity.xml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/java/io/material/demo/shrine/filters/res/values/dimens.xml b/demos/java/io/material/demo/shrine/filters/res/values/dimens.xml new file mode 100644 index 000000000..7135b50a0 --- /dev/null +++ b/demos/java/io/material/demo/shrine/filters/res/values/dimens.xml @@ -0,0 +1,21 @@ + + + + 4dp + 16sp + 12dp + diff --git a/demos/java/io/material/demo/shrine/filters/res/values/strings.xml b/demos/java/io/material/demo/shrine/filters/res/values/strings.xml new file mode 100644 index 000000000..ea1bcc10d --- /dev/null +++ b/demos/java/io/material/demo/shrine/filters/res/values/strings.xml @@ -0,0 +1,28 @@ + + + + Filters + Price Range + $1000+ + International Shipping + Personalized Feeds + Item Type + Women + Men + Girls + Boys + diff --git a/demos/java/io/material/demo/shrine/filters/res/values/styles.xml b/demos/java/io/material/demo/shrine/filters/res/values/styles.xml new file mode 100644 index 000000000..91c335227 --- /dev/null +++ b/demos/java/io/material/demo/shrine/filters/res/values/styles.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/java/io/material/demo/shrine/products/AndroidManifest.xml b/demos/java/io/material/demo/shrine/products/AndroidManifest.xml new file mode 100644 index 000000000..d48491fdc --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/AndroidManifest.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + diff --git a/demos/java/io/material/demo/shrine/products/ImageRequester.java b/demos/java/io/material/demo/shrine/products/ImageRequester.java new file mode 100644 index 000000000..5315d2a47 --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/ImageRequester.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2017 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.material.demo.shrine.products; + +import android.content.Context; +import android.graphics.Bitmap; +import android.util.DisplayMetrics; +import android.util.LruCache; +import com.android.volley.RequestQueue; +import com.android.volley.toolbox.ImageLoader; +import com.android.volley.toolbox.NetworkImageView; +import com.android.volley.toolbox.Volley; + +/** Singleton class that handles image requests using Volley. */ +public class ImageRequester { + private static Context ctx; + private static ImageRequester instance; + private final RequestQueue requestQueue; + private final ImageLoader imageLoader; + private final int maxByteSize; + + private ImageRequester(Context context) { + ctx = context; + this.requestQueue = Volley.newRequestQueue(ctx.getApplicationContext()); + this.requestQueue.start(); + this.maxByteSize = calculateMaxByteSize(); + this.imageLoader = + new ImageLoader( + requestQueue, + new ImageLoader.ImageCache() { + private final LruCache lruCache = + new LruCache(maxByteSize) { + @Override + protected int sizeOf(String url, Bitmap bitmap) { + return bitmap.getByteCount(); + } + }; + + @Override + public synchronized Bitmap getBitmap(String url) { + return lruCache.get(url); + } + + @Override + public synchronized void putBitmap(String url, Bitmap bitmap) { + lruCache.put(url, bitmap); + } + }); + instance = this; + } + + private static int calculateMaxByteSize() { + DisplayMetrics displayMetrics = ctx.getResources().getDisplayMetrics(); + final int screenBytes = displayMetrics.widthPixels * displayMetrics.heightPixels * 4; + return screenBytes * 3; + } + + /** + * Returns the existing ImageRequester instantiated, or a new instance if there is not yet an + * instance. + * + * @param context Of the calling activity. + * @return ImageRequester + */ + public static synchronized ImageRequester getInstance(Context context) { + if (instance == null) { + instance = new ImageRequester(context); + } + return instance; + } + + public void setImageFromUrl(NetworkImageView networkImageView, String url) { + networkImageView.setImageUrl(url, imageLoader); + } +} diff --git a/demos/java/io/material/demo/shrine/products/ItemActivity.java b/demos/java/io/material/demo/shrine/products/ItemActivity.java new file mode 100644 index 000000000..8c1d9e379 --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/ItemActivity.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2017 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.material.demo.shrine.products; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.design.widget.Snackbar; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.util.Log; +import android.view.Menu; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.ArrayAdapter; +import android.widget.Spinner; +import android.widget.TextView; +import com.android.volley.toolbox.NetworkImageView; + +/** + * Activity that displays an individual product, including an image, name, and a short description. + */ +public class ItemActivity extends AppCompatActivity { + private static final String PRODUCT_TITLE = "title"; + private static final String PRODUCT_URL = "url"; + private static final String PRODUCT_DESCRIPTION = "description"; + private static final String TAG = ItemActivity.class.getSimpleName(); + private String fabMessage; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.shrine_item_activity); + initToolbar(); + populateProductViews(); + initFab(fabMessage); + } + + public static void createItemActivityIntent(Context context, ProductEntry productEntry) { + Intent intent = new Intent(context, ItemActivity.class); + intent.putExtra(PRODUCT_TITLE, productEntry.title); + intent.putExtra(PRODUCT_URL, productEntry.url); + intent.putExtra(PRODUCT_DESCRIPTION, productEntry.description); + context.startActivity(intent); + } + + private void populateProductViews() { + Bundle bundle = getIntent().getExtras(); + if (bundle == null) { + String error = "Error retrieving product information."; + Log.e(TAG, error); + Snackbar.make(findViewById(R.id.ProductTitle), error, Snackbar.LENGTH_SHORT).show(); + fabMessage = error; + } else { + TextView productTitle = (TextView) findViewById(R.id.ProductTitle); + productTitle.setText(bundle.getString(PRODUCT_TITLE)); + TextView productDescription = (TextView) findViewById(R.id.ProductDescription); + productDescription.setText(bundle.getString(PRODUCT_DESCRIPTION)); + initSpinner(); + NetworkImageView productImage = (NetworkImageView) findViewById(R.id.ProductImage); + ImageRequester.getInstance(this).setImageFromUrl(productImage, bundle.getString(PRODUCT_URL)); + fabMessage = getString(R.string.shrine_product_added_message); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.shrine_toolbar_menu, menu); + menu.findItem(R.id.ShrineToolbarFilterIcon).setVisible(false); + return true; + } + + private void initToolbar() { + Toolbar toolbar = (Toolbar) findViewById(R.id.AppBar); + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } + + private void initFab(String fabMessage) { + findViewById(R.id.FloatingActionButton) + .setOnClickListener( + new OnClickListener() { + @Override + public void onClick(View v) { + Snackbar.make(v, fabMessage, Snackbar.LENGTH_SHORT).show(); + } + }); + } + + private void initSpinner() { + Spinner spinner = (Spinner) findViewById(R.id.QuantitySpinner); + ArrayAdapter adapter = + ArrayAdapter.createFromResource( + this, R.array.product_quantities, R.layout.shrine_spinner_item); + adapter.setDropDownViewResource(R.layout.shrine_spinner_dropdown); + spinner.setAdapter(adapter); + } +} diff --git a/demos/java/io/material/demo/shrine/products/ProductEntry.java b/demos/java/io/material/demo/shrine/products/ProductEntry.java new file mode 100644 index 000000000..4e1a2f6c9 --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/ProductEntry.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 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.material.demo.shrine.products; + +import android.net.Uri; + +/** A product entry in the list of products. */ +public class ProductEntry { + public final String title; + public final Uri dynamicUrl; + public final String url; + public final String price; + public final String description; + + public ProductEntry( + String title, String dynamicUrl, String url, String price, String description) { + this.title = title; + this.dynamicUrl = Uri.parse(dynamicUrl); + this.url = url; + this.price = price; + this.description = description; + } +} diff --git a/demos/java/io/material/demo/shrine/products/ProductListActivity.java b/demos/java/io/material/demo/shrine/products/ProductListActivity.java new file mode 100644 index 000000000..87f58fd66 --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/ProductListActivity.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2017 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.material.demo.shrine.products; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.graphics.Point; +import android.os.Bundle; +import android.support.design.widget.CollapsingToolbarLayout; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.CardView; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.RecyclerView.Adapter; +import android.support.v7.widget.RecyclerView.ViewHolder; +import android.support.v7.widget.Toolbar; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.TextView; +import com.android.volley.toolbox.NetworkImageView; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import io.material.demo.shrine.filters.FiltersActivity; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +/** Activity that displays a list of products. */ +public class ProductListActivity extends AppCompatActivity { + private static final String TAG = ProductListActivity.class.getSimpleName(); + private ImageRequester imageRequester; + private List productEntryList; + + @Override + public void onCreate(Bundle savedInstanceState) { + setTheme(R.style.Theme_Shrine); + super.onCreate(savedInstanceState); + setContentView(R.layout.shrine_product_activity); + imageRequester = ImageRequester.getInstance(this); + initProductEntryList(); + initCollapsingToolbar(); + initItemGrid(); + } + + private void initProductEntryList() { + InputStream inputStream = getResources().openRawResource(R.raw.products); + Writer writer = new StringWriter(); + char[] buffer = new char[1024]; + try { + Reader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); + int pointer; + while ((pointer = reader.read(buffer)) != -1) { + writer.write(buffer, 0, pointer); + } + } catch (IOException exception) { + Log.e(TAG, "Error writing/reading from the JSON file.", exception); + } finally { + try { + inputStream.close(); + } catch (IOException exception) { + Log.e(TAG, "Error closing the input stream.", exception); + } + } + String jsonProductsString = writer.toString(); + Gson gson = new Gson(); + Type productListType = new TypeToken>() {}.getType(); + this.productEntryList = gson.fromJson(jsonProductsString, productListType); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.shrine_toolbar_menu, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem menuItem) { + if (menuItem.getItemId() == R.id.ShrineToolbarFilterIcon) { + startActivity(new Intent(this, FiltersActivity.class)); + return true; + } + return false; + } + + private void initCollapsingToolbar() { + Toolbar toolbar = (Toolbar) findViewById(R.id.AppBar); + int collapsingToolbarImageIndex = 6; + int collapsingImageToolbarSize = 1200; + NetworkImageView collapsingToolbarImage = + (NetworkImageView) findViewById(R.id.CollapsingToolbarImage); + StringBuilder collapsingToolbarImageUrl = + new StringBuilder(productEntryList.get(collapsingToolbarImageIndex).url); + collapsingToolbarImageUrl.append("=s"); + collapsingToolbarImageUrl.append(collapsingImageToolbarSize); + setSupportActionBar(toolbar); + CollapsingToolbarLayout collapsingToolbarLayout = + (CollapsingToolbarLayout) findViewById(R.id.CollapsingToolbarLayout); + collapsingToolbarLayout.setTitle(toolbar.getTitle()); + collapsingToolbarLayout.setExpandedTitleTextAppearance(R.style.TextAppearance_Shrine_Logo); + collapsingToolbarLayout.setCollapsedTitleTextAppearance(R.style.TextAppearance_Shrine_Logo); + imageRequester.setImageFromUrl(collapsingToolbarImage, collapsingToolbarImageUrl.toString()); + Point windowSize = new Point(); + getWindowManager().getDefaultDisplay().getSize(windowSize); + int windowWidth = windowSize.x; + collapsingToolbarImage.setX(collapsingToolbarImage.getX() - windowWidth / 4); + collapsingToolbarLayout.setScrimVisibleHeightTrigger( + (int) getResources().getDimension(R.dimen.shrine_tall_toolbar_height) / 2); + } + + private void initItemGrid() { + RecyclerView recyclerView = (RecyclerView) findViewById(R.id.ProductGrid); + recyclerView.setHasFixedSize(true); + recyclerView.setLayoutManager(new GridLayoutManager(this, 2)); + recyclerView.setAdapter(new ProductAdapter(this, productEntryList, imageRequester)); + } + + private static class ProductAdapter extends Adapter { + private final Activity activity; + private final List productEntries; + public ImageRequester imageRequester; + + public ProductAdapter( + Activity activity, List productEntries, ImageRequester imageRequester) { + this.activity = activity; + this.productEntries = productEntries; + this.imageRequester = imageRequester; + } + + @Override + public ProductViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { + return new ProductViewHolder(activity, viewGroup); + } + + @Override + public void onBindViewHolder(ProductViewHolder productViewHolder, int i) { + productViewHolder.bind(activity, productEntries.get(i), imageRequester); + } + + @Override + public int getItemCount() { + return productEntries.size(); + } + } + + private static class ProductViewHolder extends ViewHolder { + private final TextView productPriceView; + private final NetworkImageView productImageView; + private final TextView productShopNameView; + private final CardView productEntryView; + + public ProductViewHolder(Context context, ViewGroup parent) { + super(LayoutInflater.from(context).inflate(R.layout.shrine_product_entry, parent, false)); + ViewGroup.LayoutParams layoutParams = itemView.getLayoutParams(); + itemView.setLayoutParams(layoutParams); + productPriceView = (TextView) itemView.findViewById(R.id.ProductPrice); + productImageView = (NetworkImageView) itemView.findViewById(R.id.ProductImage); + productShopNameView = (TextView) itemView.findViewById(R.id.ProductShopName); + productEntryView = (CardView) itemView.findViewById(R.id.ProductEntry); + productEntryView.setOnClickListener(clickListener); + } + + private final OnClickListener clickListener = + new OnClickListener() { + @Override + public void onClick(View v) { + ItemActivity.createItemActivityIntent(v.getContext(), (ProductEntry) v.getTag()); + } + }; + + public void bind(Context context, ProductEntry productEntry, ImageRequester imageRequester) { + productPriceView.setText(productEntry.price); + imageRequester.setImageFromUrl(productImageView, productEntry.url); + productShopNameView.setText(productEntry.title); + productEntryView.setTag(productEntry); + } + } +} diff --git a/demos/java/io/material/demo/shrine/products/res/drawable/ic_add_shopping_cart_vd_theme_24.xml b/demos/java/io/material/demo/shrine/products/res/drawable/ic_add_shopping_cart_vd_theme_24.xml new file mode 100644 index 000000000..a8d439dd1 --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/res/drawable/ic_add_shopping_cart_vd_theme_24.xml @@ -0,0 +1,25 @@ + + + + diff --git a/demos/java/io/material/demo/shrine/products/res/drawable/ic_arrow_drop_down_vd_theme_24.xml b/demos/java/io/material/demo/shrine/products/res/drawable/ic_arrow_drop_down_vd_theme_24.xml new file mode 100644 index 000000000..b3a76d067 --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/res/drawable/ic_arrow_drop_down_vd_theme_24.xml @@ -0,0 +1,25 @@ + + + + diff --git a/demos/java/io/material/demo/shrine/products/res/drawable/ic_favorite_vd_theme_24.xml b/demos/java/io/material/demo/shrine/products/res/drawable/ic_favorite_vd_theme_24.xml new file mode 100644 index 000000000..2f8da7bed --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/res/drawable/ic_favorite_vd_theme_24.xml @@ -0,0 +1,25 @@ + + + + diff --git a/demos/java/io/material/demo/shrine/products/res/drawable/ic_filter_list_vd_theme_24.xml b/demos/java/io/material/demo/shrine/products/res/drawable/ic_filter_list_vd_theme_24.xml new file mode 100644 index 000000000..88e114552 --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/res/drawable/ic_filter_list_vd_theme_24.xml @@ -0,0 +1,25 @@ + + + + diff --git a/demos/java/io/material/demo/shrine/products/res/drawable/ic_search_vd_theme_24.xml b/demos/java/io/material/demo/shrine/products/res/drawable/ic_search_vd_theme_24.xml new file mode 100644 index 000000000..c88816ee8 --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/res/drawable/ic_search_vd_theme_24.xml @@ -0,0 +1,25 @@ + + + + diff --git a/demos/java/io/material/demo/shrine/products/res/drawable/ic_shopping_cart_vd_theme_24.xml b/demos/java/io/material/demo/shrine/products/res/drawable/ic_shopping_cart_vd_theme_24.xml new file mode 100644 index 000000000..ca19ddc0e --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/res/drawable/ic_shopping_cart_vd_theme_24.xml @@ -0,0 +1,25 @@ + + + + diff --git a/demos/java/io/material/demo/shrine/products/res/drawable/ic_shrine_vd_theme_24.xml b/demos/java/io/material/demo/shrine/products/res/drawable/ic_shrine_vd_theme_24.xml new file mode 100644 index 000000000..889b509df --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/res/drawable/ic_shrine_vd_theme_24.xml @@ -0,0 +1,25 @@ + + + + diff --git a/demos/java/io/material/demo/shrine/products/res/drawable/shrine_spinner_border.xml b/demos/java/io/material/demo/shrine/products/res/drawable/shrine_spinner_border.xml new file mode 100644 index 000000000..d92309a33 --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/res/drawable/shrine_spinner_border.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/demos/java/io/material/demo/shrine/products/res/layout/shrine_item_activity.xml b/demos/java/io/material/demo/shrine/products/res/layout/shrine_item_activity.xml new file mode 100644 index 000000000..8587ebd2b --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/res/layout/shrine_item_activity.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/java/io/material/demo/shrine/products/res/layout/shrine_product_activity.xml b/demos/java/io/material/demo/shrine/products/res/layout/shrine_product_activity.xml new file mode 100644 index 000000000..62900548b --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/res/layout/shrine_product_activity.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + diff --git a/demos/java/io/material/demo/shrine/products/res/layout/shrine_product_entry.xml b/demos/java/io/material/demo/shrine/products/res/layout/shrine_product_entry.xml new file mode 100644 index 000000000..f301d1807 --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/res/layout/shrine_product_entry.xml @@ -0,0 +1,51 @@ + + + + + + + + + diff --git a/demos/java/io/material/demo/shrine/products/res/layout/shrine_spinner_dropdown.xml b/demos/java/io/material/demo/shrine/products/res/layout/shrine_spinner_dropdown.xml new file mode 100644 index 000000000..d53039de3 --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/res/layout/shrine_spinner_dropdown.xml @@ -0,0 +1,18 @@ + + + diff --git a/demos/java/io/material/demo/shrine/products/res/layout/shrine_spinner_item.xml b/demos/java/io/material/demo/shrine/products/res/layout/shrine_spinner_item.xml new file mode 100644 index 000000000..5ac3b6242 --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/res/layout/shrine_spinner_item.xml @@ -0,0 +1,20 @@ + + + diff --git a/demos/java/io/material/demo/shrine/products/res/menu/shrine_bottom_nav_view_menu.xml b/demos/java/io/material/demo/shrine/products/res/menu/shrine_bottom_nav_view_menu.xml new file mode 100644 index 000000000..7306c5f53 --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/res/menu/shrine_bottom_nav_view_menu.xml @@ -0,0 +1,37 @@ + + + + + + + diff --git a/demos/java/io/material/demo/shrine/products/res/menu/shrine_toolbar_menu.xml b/demos/java/io/material/demo/shrine/products/res/menu/shrine_toolbar_menu.xml new file mode 100644 index 000000000..7e60667c7 --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/res/menu/shrine_toolbar_menu.xml @@ -0,0 +1,29 @@ + + + + + + diff --git a/demos/java/io/material/demo/shrine/products/res/raw/products.json b/demos/java/io/material/demo/shrine/products/res/raw/products.json new file mode 100644 index 000000000..1c4ed27cc --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/res/raw/products.json @@ -0,0 +1,74 @@ +[ + { + "title": "Red Popsicle", + "url": "https://lh3.googleusercontent.com/3w0neXbSlI4p6mtkRdwL7SwRH4NKHmmdBm1n58tNvUnV0DwnxZ6MfIwzSz0c7MlE3NZku1TmQAQm-4Tly6geteQpONOniM_m", + "price": "$5", + "description": "Looks can be deceiving. This Red Popsicle comes in a wide variety of flavors, including strawberry, that burst as soon as it hits the mouth. Red Popsicles melt slow, so savor the flavor." + }, + { + "title": "Green Slip-ons", + "url": "https://lh3.googleusercontent.com/7O1nZyD9xwP0nel27kC49ZVfuUlMJrGdcI1j8PHBZxD2J8Gd-q1wQ5Rrn9sDQ_Q7Zi1-SNHDrBRaB5FD5zbtoEto1RpjBkXo", + "price": "$75", + "description": "Feetsy has been making extraordinary slip-ons for decades. With each pair of shoes purchased Feetsy donates a pair to those in need. Buy yourself a pair, buy someone else a pair. Very Comfortable." + }, + { + "title": "Teapot", + "url": "https://lh3.googleusercontent.com/2L6RmCDcdBT_5160bg-rZaybSCGze6TCpEyTYko0Y6GWVp432qttKvXB19w4TgSnTz-Pxes12TPSzdRWT8Gq9dCJm2aF1zPj8g", + "price": "$210", + "description": "Impress your guests with Teapot by Kitchen Stuff. Teapot holds extremely hot liquids and pours them from the spout. Use the handle, shown on the left, so your fingers don’t get burnt while pouring." + }, + { + "title": "Beachball", + "url": "https://lh3.googleusercontent.com/FDlLqxdGjraCR8J0QzcuCh7eRHvnXNyWwP9mOTMviY2IX4WglCbAobxBD8tfYvUk-gZeOwvv02IC5vhbgEAk7F7gV2XwLJpAaw", + "price": "$25", + "description": "Are you at a baseball game and feeling bored? At a pool party and looking for a laugh? Do you need something to take your anger out on? Beachball, by inflatable fun, is the perfect outlet." + }, + { + "title": "Clock", + "url": "https://lh3.googleusercontent.com/RJIsp5e-fQ_oQBWR-OJfJgcvCmfFJ8ZCL5D5_4-P3U4h7Im-WDvVJYqTl_YsCWcKFn5xbLI_9flEzUr0CA1PwCYGqDQDLvKxDA", + "price": "$120", + "description": "Timekeeper Co makes clocks that tell time precisely. Clock is very simple to use, set the time using your phone, hang it, and viola! You’ll never be late again." + }, + { + "title": "Lime Flippers", + "url": "https://lh3.googleusercontent.com/74P9JRtvjF4IIUYLxTZEFp9ffjNrcTFE1pCPduyTLPNjTAldtsRKn0iDx1CmOclJIsYJUWpyCKqXFrJzv3-ebEDe62wLci0R", + "price": "$25", + "description": "Flippers are a nice tool to have when you’re being chased by an oversized sea turtle. Never get caught again with these fast water shoes. You’re like a fish, but more graceful." + }, + { + "title": "Perfect Goldfish \nBowl", + "url": "https://lh3.googleusercontent.com/pfgpUuVo_9zWqn2lI6gAlDK2ceLe_MEzgNtj-RaClJb0w41NvSrOg-xgwMcr5FrYTdC6wJHKDSdP7GiuSbNAdjTSHlOdfXR1", + "price": "$25", + "description": "The Perfect Bowl Co makes the best bowls for just about anything you can think of. This Perfect Goldfish Bowl holds water and fish perfectly. Looks great in living rooms. Keep out of reach from cats." + }, + { + "title": "Surfboard", + "url": "https://lh3.googleusercontent.com/585zsoqfGl58nbCDgM3MXLcVcZCfriN1k4N7KTnHDBULhSmeF9D80HhLAuDixrk0gLp9YdArxCQfZ7Yg-3zlEgkvQBaXzBwYSg", + "price": "$300", + "description": "Who says you can’t walk on water? With Surfboard, by Surfboard Supply, you can fly on water. This beast is fast and handles like a porsche. Hang Ten Bro!" + }, + { + "title": "Backpack", + "url": "https://lh3.googleusercontent.com/KE89imsb_0hMmO2YMxnZ6cQYelrmI3jdpDo06svFeNj_OjHUw9dfHoBj4pN7qqm1khs9-_japkG5As6TWgOmJzTa8Cv_9GcGfg", + "price": "$150", + "description": "This backpack by Bags ‘n’ stuff can hold just about anything: a laptop, a pen, a protractor, notebooks, small animals, plugs for your devices, sunglasses, gym clothes, shoes, gloves, two kittens, and even lunch!" + }, + { + "title": "Sunglasses", + "url": "https://lh3.googleusercontent.com/-0TR98kQUVgeSGyK24T0181IwtNgCWhxfMejw-fEXQF005lfuDERc-B6hxtSPBsPyFEJBxPv8H9FSNb7k3AgjRO2wm8YXeMa", + "price": "$100", + "description": "Be an optimist. Carry Sunglasses with you at all times. All Tints and Shades products come with polarized lenses and super duper UV protection so you can look at the sun for however long you want. Sunglasses make you look cool, wear them." + }, + { + "title": "Red Lipstick Set", + "url": "https://lh3.googleusercontent.com/w7BggGOgZEVuI4_wsSO96PDi-y4kqtHVHqQknCMN7eyraQLIlh58QK5Y1Wc-eTAYDcmn7KmiJNvKsoFLAZBClSAYpI9vyuDRIw", + "price": "$25", + "description": "Trying to find the perfect shade to match your mood? Try no longer. Red Lipstick Set by StickLips has you covered for those nights when you need to get out, or even if you’re just headed to work." + }, + { + "title": "Dipped Brush", + "url": "https://lh3.googleusercontent.com/8dsHE4FwZ5VJOFj_EmhGt48r19ILh7rloMCBC5QPyP2Jrm7-Jz4wVZa1Q3cGFBBbi8OKJzb7nwUnBOqaPVwafMKOthbRtv6Z", + "price": "$15", + "description": "WeDipIt does it again. This handle dipped 4 inch brush is a perfect for painting 4 inch lines, or coloring in big areas with paint. Just be sure you don’t drop it in a bucket of red paint, then it won’t look dipped anymore." + } +] diff --git a/demos/java/io/material/demo/shrine/products/res/values/colors.xml b/demos/java/io/material/demo/shrine/products/res/values/colors.xml new file mode 100644 index 000000000..166eb4ae7 --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/res/values/colors.xml @@ -0,0 +1,21 @@ + + + + #ffb300 + #1f000000 + #0a3142 + diff --git a/demos/java/io/material/demo/shrine/products/res/values/dimens.xml b/demos/java/io/material/demo/shrine/products/res/values/dimens.xml new file mode 100644 index 000000000..8d10ac280 --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/res/values/dimens.xml @@ -0,0 +1,34 @@ + + + + 474dp + 48dp + 124dp + 124dp + 232dp + 124dp + 8dp + 4dp + 12dp + 200dp + 292dp + 30sp + 56dp + 212dp + 48dp + 2dp + diff --git a/demos/java/io/material/demo/shrine/products/res/values/strings.xml b/demos/java/io/material/demo/shrine/products/res/values/strings.xml new file mode 100644 index 000000000..e279b0284 --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/res/values/strings.xml @@ -0,0 +1,37 @@ + + + + Best Gifts for Teen Girls + + Leave the tunnel and the rain is fallin amazing things happen when you wait + + Product was added to your cart. + + Favorites + Home + Cart + + Search + Filter + + + Quantity 1 + Quantity 2 + Quantity 3 + Quantity 4 + + diff --git a/demos/java/io/material/demo/shrine/products/res/values/styles.xml b/demos/java/io/material/demo/shrine/products/res/values/styles.xml new file mode 100644 index 000000000..1a9f06e1b --- /dev/null +++ b/demos/java/io/material/demo/shrine/products/res/values/styles.xml @@ -0,0 +1,37 @@ + + + + + + + + + + diff --git a/demos/java/io/material/demo/shrine/res/values/strings.xml b/demos/java/io/material/demo/shrine/res/values/strings.xml new file mode 100644 index 000000000..bacc315d7 --- /dev/null +++ b/demos/java/io/material/demo/shrine/res/values/strings.xml @@ -0,0 +1,19 @@ + + + + Shrine + diff --git a/demos/java/io/material/demo/shrine/theme/AndroidManifest.xml b/demos/java/io/material/demo/shrine/theme/AndroidManifest.xml new file mode 100644 index 000000000..97244988e --- /dev/null +++ b/demos/java/io/material/demo/shrine/theme/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + + + + diff --git a/demos/java/io/material/demo/shrine/theme/res/drawable-hdpi/product_logo_shrine_color_144.png b/demos/java/io/material/demo/shrine/theme/res/drawable-hdpi/product_logo_shrine_color_144.png new file mode 100644 index 000000000..28e5a7d7b Binary files /dev/null and b/demos/java/io/material/demo/shrine/theme/res/drawable-hdpi/product_logo_shrine_color_144.png differ diff --git a/demos/java/io/material/demo/shrine/theme/res/drawable-hdpi/product_logo_shrine_color_192.png b/demos/java/io/material/demo/shrine/theme/res/drawable-hdpi/product_logo_shrine_color_192.png new file mode 100644 index 000000000..7cf6ea239 Binary files /dev/null and b/demos/java/io/material/demo/shrine/theme/res/drawable-hdpi/product_logo_shrine_color_192.png differ diff --git a/demos/java/io/material/demo/shrine/theme/res/drawable-mdpi/product_logo_shrine_color_144.png b/demos/java/io/material/demo/shrine/theme/res/drawable-mdpi/product_logo_shrine_color_144.png new file mode 100644 index 000000000..a56c7f511 Binary files /dev/null and b/demos/java/io/material/demo/shrine/theme/res/drawable-mdpi/product_logo_shrine_color_144.png differ diff --git a/demos/java/io/material/demo/shrine/theme/res/drawable-mdpi/product_logo_shrine_color_192.png b/demos/java/io/material/demo/shrine/theme/res/drawable-mdpi/product_logo_shrine_color_192.png new file mode 100644 index 000000000..bd5e5dfce Binary files /dev/null and b/demos/java/io/material/demo/shrine/theme/res/drawable-mdpi/product_logo_shrine_color_192.png differ diff --git a/demos/java/io/material/demo/shrine/theme/res/drawable-sw600dp/launch_screen.xml b/demos/java/io/material/demo/shrine/theme/res/drawable-sw600dp/launch_screen.xml new file mode 100644 index 000000000..c280f39e3 --- /dev/null +++ b/demos/java/io/material/demo/shrine/theme/res/drawable-sw600dp/launch_screen.xml @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/demos/java/io/material/demo/shrine/theme/res/drawable-xhdpi/product_logo_shrine_color_144.png b/demos/java/io/material/demo/shrine/theme/res/drawable-xhdpi/product_logo_shrine_color_144.png new file mode 100644 index 000000000..7cf6ea239 Binary files /dev/null and b/demos/java/io/material/demo/shrine/theme/res/drawable-xhdpi/product_logo_shrine_color_144.png differ diff --git a/demos/java/io/material/demo/shrine/theme/res/drawable-xhdpi/product_logo_shrine_color_192.png b/demos/java/io/material/demo/shrine/theme/res/drawable-xhdpi/product_logo_shrine_color_192.png new file mode 100644 index 000000000..9ddb0d2b4 Binary files /dev/null and b/demos/java/io/material/demo/shrine/theme/res/drawable-xhdpi/product_logo_shrine_color_192.png differ diff --git a/demos/java/io/material/demo/shrine/theme/res/drawable-xxhdpi/product_logo_shrine_color_144.png b/demos/java/io/material/demo/shrine/theme/res/drawable-xxhdpi/product_logo_shrine_color_144.png new file mode 100644 index 000000000..734a8c719 Binary files /dev/null and b/demos/java/io/material/demo/shrine/theme/res/drawable-xxhdpi/product_logo_shrine_color_144.png differ diff --git a/demos/java/io/material/demo/shrine/theme/res/drawable-xxhdpi/product_logo_shrine_color_192.png b/demos/java/io/material/demo/shrine/theme/res/drawable-xxhdpi/product_logo_shrine_color_192.png new file mode 100644 index 000000000..8af7ba836 Binary files /dev/null and b/demos/java/io/material/demo/shrine/theme/res/drawable-xxhdpi/product_logo_shrine_color_192.png differ diff --git a/demos/java/io/material/demo/shrine/theme/res/drawable-xxxhdpi/product_logo_shrine_color_144.png b/demos/java/io/material/demo/shrine/theme/res/drawable-xxxhdpi/product_logo_shrine_color_144.png new file mode 100644 index 000000000..8af7ba836 Binary files /dev/null and b/demos/java/io/material/demo/shrine/theme/res/drawable-xxxhdpi/product_logo_shrine_color_144.png differ diff --git a/demos/java/io/material/demo/shrine/theme/res/drawable-xxxhdpi/product_logo_shrine_color_192.png b/demos/java/io/material/demo/shrine/theme/res/drawable-xxxhdpi/product_logo_shrine_color_192.png new file mode 100644 index 000000000..c7dc9e318 Binary files /dev/null and b/demos/java/io/material/demo/shrine/theme/res/drawable-xxxhdpi/product_logo_shrine_color_192.png differ diff --git a/demos/java/io/material/demo/shrine/theme/res/drawable/launch_screen.xml b/demos/java/io/material/demo/shrine/theme/res/drawable/launch_screen.xml new file mode 100644 index 000000000..e6084005e --- /dev/null +++ b/demos/java/io/material/demo/shrine/theme/res/drawable/launch_screen.xml @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/demos/java/io/material/demo/shrine/theme/res/mipmap-hdpi/product_logo_shrine_launcher_color_48.png b/demos/java/io/material/demo/shrine/theme/res/mipmap-hdpi/product_logo_shrine_launcher_color_48.png new file mode 100644 index 000000000..b7ae17c92 Binary files /dev/null and b/demos/java/io/material/demo/shrine/theme/res/mipmap-hdpi/product_logo_shrine_launcher_color_48.png differ diff --git a/demos/java/io/material/demo/shrine/theme/res/mipmap-mdpi/product_logo_shrine_launcher_color_48.png b/demos/java/io/material/demo/shrine/theme/res/mipmap-mdpi/product_logo_shrine_launcher_color_48.png new file mode 100644 index 000000000..641263ab4 Binary files /dev/null and b/demos/java/io/material/demo/shrine/theme/res/mipmap-mdpi/product_logo_shrine_launcher_color_48.png differ diff --git a/demos/java/io/material/demo/shrine/theme/res/mipmap-xhdpi/product_logo_shrine_launcher_color_48.png b/demos/java/io/material/demo/shrine/theme/res/mipmap-xhdpi/product_logo_shrine_launcher_color_48.png new file mode 100644 index 000000000..416413974 Binary files /dev/null and b/demos/java/io/material/demo/shrine/theme/res/mipmap-xhdpi/product_logo_shrine_launcher_color_48.png differ diff --git a/demos/java/io/material/demo/shrine/theme/res/mipmap-xxhdpi/product_logo_shrine_launcher_color_48.png b/demos/java/io/material/demo/shrine/theme/res/mipmap-xxhdpi/product_logo_shrine_launcher_color_48.png new file mode 100644 index 000000000..ad5a1747c Binary files /dev/null and b/demos/java/io/material/demo/shrine/theme/res/mipmap-xxhdpi/product_logo_shrine_launcher_color_48.png differ diff --git a/demos/java/io/material/demo/shrine/theme/res/mipmap-xxxhdpi/product_logo_shrine_launcher_color_48.png b/demos/java/io/material/demo/shrine/theme/res/mipmap-xxxhdpi/product_logo_shrine_launcher_color_48.png new file mode 100644 index 000000000..469f6a5b2 Binary files /dev/null and b/demos/java/io/material/demo/shrine/theme/res/mipmap-xxxhdpi/product_logo_shrine_launcher_color_48.png differ diff --git a/demos/java/io/material/demo/shrine/theme/res/values-land/dimens.xml b/demos/java/io/material/demo/shrine/theme/res/values-land/dimens.xml new file mode 100644 index 000000000..758a8b887 --- /dev/null +++ b/demos/java/io/material/demo/shrine/theme/res/values-land/dimens.xml @@ -0,0 +1,20 @@ + + + + + 48dp + diff --git a/demos/java/io/material/demo/shrine/theme/res/values-port-v21/dimens.xml b/demos/java/io/material/demo/shrine/theme/res/values-port-v21/dimens.xml new file mode 100644 index 000000000..ef9bd80e3 --- /dev/null +++ b/demos/java/io/material/demo/shrine/theme/res/values-port-v21/dimens.xml @@ -0,0 +1,20 @@ + + + + + 64dp + diff --git a/demos/java/io/material/demo/shrine/theme/res/values-sw600dp-land-v21/dimens.xml b/demos/java/io/material/demo/shrine/theme/res/values-sw600dp-land-v21/dimens.xml new file mode 100644 index 000000000..5e71e97f6 --- /dev/null +++ b/demos/java/io/material/demo/shrine/theme/res/values-sw600dp-land-v21/dimens.xml @@ -0,0 +1,20 @@ + + + + + 72dp + diff --git a/demos/java/io/material/demo/shrine/theme/res/values-sw600dp-land/dimens.xml b/demos/java/io/material/demo/shrine/theme/res/values-sw600dp-land/dimens.xml new file mode 100644 index 000000000..dcc05190c --- /dev/null +++ b/demos/java/io/material/demo/shrine/theme/res/values-sw600dp-land/dimens.xml @@ -0,0 +1,20 @@ + + + + + 24dp + diff --git a/demos/java/io/material/demo/shrine/theme/res/values-sw600dp-port-v21/dimens.xml b/demos/java/io/material/demo/shrine/theme/res/values-sw600dp-port-v21/dimens.xml new file mode 100644 index 000000000..5e71e97f6 --- /dev/null +++ b/demos/java/io/material/demo/shrine/theme/res/values-sw600dp-port-v21/dimens.xml @@ -0,0 +1,20 @@ + + + + + 72dp + diff --git a/demos/java/io/material/demo/shrine/theme/res/values-sw600dp/dimens.xml b/demos/java/io/material/demo/shrine/theme/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..934c551ee --- /dev/null +++ b/demos/java/io/material/demo/shrine/theme/res/values-sw600dp/dimens.xml @@ -0,0 +1,19 @@ + + + + 24dp + diff --git a/demos/java/io/material/demo/shrine/theme/res/values/colors.xml b/demos/java/io/material/demo/shrine/theme/res/values/colors.xml new file mode 100644 index 000000000..bbe7a08e9 --- /dev/null +++ b/demos/java/io/material/demo/shrine/theme/res/values/colors.xml @@ -0,0 +1,23 @@ + + + + #ffffff + #e0e0e0 + #607d8b + #dd000000 + #89000000 + diff --git a/demos/java/io/material/demo/shrine/theme/res/values/dimens.xml b/demos/java/io/material/demo/shrine/theme/res/values/dimens.xml new file mode 100644 index 000000000..5f2536ced --- /dev/null +++ b/demos/java/io/material/demo/shrine/theme/res/values/dimens.xml @@ -0,0 +1,26 @@ + + + + 16dp + 8dp + 36dp + 72dp + 4dp + 16dp + 56dp + 16dp + diff --git a/demos/java/io/material/demo/shrine/theme/res/values/styles.xml b/demos/java/io/material/demo/shrine/theme/res/values/styles.xml new file mode 100644 index 000000000..f200e2a2d --- /dev/null +++ b/demos/java/io/material/demo/shrine/theme/res/values/styles.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + diff --git a/demos/javatests/io/material/demo/shrine/products/ProductListActivityTest.java b/demos/javatests/io/material/demo/shrine/products/ProductListActivityTest.java new file mode 100644 index 000000000..270b8389c --- /dev/null +++ b/demos/javatests/io/material/demo/shrine/products/ProductListActivityTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 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.material.demo.shrine.products; + +import static com.google.common.truth.Truth.assertThat; + +import android.support.v7.widget.RecyclerView; +import org.robolectric.RobolectricTestRunner; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.annotation.Config; + +/** Unit tests for {@link ProductListActivity}. */ +@RunWith(RobolectricTestRunner.class) +@Config( + manifest = + "//third_party/java_src/android_libs/material_components/demos/java/io/material/demo/shrine/products" + ":products/AndroidManifest.xml" +) +public class ProductListActivityTest { + + private ProductListActivity productListActivity; + private RecyclerView productsRecyclerView; + + @Before + public void setUp() { + productListActivity = Robolectric.setupActivity(ProductListActivity.class); + productsRecyclerView = (RecyclerView) productListActivity.findViewById(R.id.ProductGrid); + } + + @Test + public void testProductListIsNotNull() { + assertThat(productsRecyclerView).isNotNull(); + } + + @Test + public void testProductListHasProducts() { + int productCount = productsRecyclerView.getChildCount(); + assertThat(productCount).isAtLeast(1); + } +}