/* * 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.ui; import android.app.Activity; import android.content.res.Resources; import android.net.Uri; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.customtabs.CustomTabsIntent; import android.support.v4.content.ContextCompat; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.widget.RecyclerView; import android.text.Layout; import android.text.SpannableString; import android.text.Spanned; import android.text.TextUtils; import android.text.style.AlignmentSpan; import android.transition.TransitionInflater; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; import com.bumptech.glide.DrawableRequestBuilder; import com.bumptech.glide.Glide; import java.security.InvalidParameterException; import butterknife.BindView; import butterknife.ButterKnife; import in.uncod.android.bypass.Bypass; import io.plaidapp.R; import io.plaidapp.ui.widget.ElasticDragDismissFrameLayout; import io.plaidapp.ui.widget.InkPageIndicator; import io.plaidapp.util.HtmlUtils; import io.plaidapp.util.customtabs.CustomTabActivityHelper; import io.plaidapp.util.glide.CircleTransform; /** * About screen. This displays 3 pages in a ViewPager: * – About Plaid * – Credit Roman for the awesome icon * – Credit libraries */ public class AboutActivity extends Activity { @BindView(R.id.draggable_frame) ElasticDragDismissFrameLayout draggableFrame; @BindView(R.id.pager) ViewPager pager; @BindView(R.id.indicator) InkPageIndicator pageIndicator; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_about); ButterKnife.bind(this); pager.setAdapter(new AboutPagerAdapter(AboutActivity.this)); pager.setPageMargin(getResources().getDimensionPixelSize(R.dimen.spacing_normal)); pageIndicator.setViewPager(pager); draggableFrame.addListener( new ElasticDragDismissFrameLayout.SystemChromeFader(this) { @Override public void onDragDismissed() { // if we drag dismiss downward then the default reversal of the enter // transition would slide content upward which looks weird. So reverse it. if (draggableFrame.getTranslationY() > 0) { getWindow().setReturnTransition( TransitionInflater.from(AboutActivity.this) .inflateTransition(R.transition.about_return_downward)); } finishAfterTransition(); } }); } static class AboutPagerAdapter extends PagerAdapter { private View aboutPlaid; @Nullable @BindView(R.id.about_description) TextView plaidDescription; private View aboutIcon; @Nullable @BindView(R.id.icon_description) TextView iconDescription; private View aboutLibs; @Nullable @BindView(R.id.libs_list) RecyclerView libsList; private final LayoutInflater layoutInflater; private final Bypass markdown; private final Activity host; private final Resources resources; AboutPagerAdapter(@NonNull Activity host) { this.host = host; resources = host.getResources(); layoutInflater = LayoutInflater.from(host); markdown = new Bypass(host, new Bypass.Options()); } @Override public Object instantiateItem(ViewGroup collection, int position) { View layout = getPage(position, collection); collection.addView(layout); return layout; } @Override public void destroyItem(ViewGroup collection, int position, Object view) { collection.removeView((View) view); } @Override public int getCount() { return 3; } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } private View getPage(int position, ViewGroup parent) { switch (position) { case 0: if (aboutPlaid == null) { aboutPlaid = layoutInflater.inflate(R.layout.about_plaid, parent, false); ButterKnife.bind(this, aboutPlaid); // fun with spans & markdown CharSequence about0 = markdown.markdownToSpannable(resources .getString(R.string.about_plaid_0), plaidDescription, null); SpannableString about1 = new SpannableString( resources.getString(R.string.about_plaid_1)); about1.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER), 0, about1.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); SpannableString about2 = new SpannableString(markdown.markdownToSpannable (resources.getString(R.string.about_plaid_2), plaidDescription, null)); about2.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER), 0, about2.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); SpannableString about3 = new SpannableString(markdown.markdownToSpannable (resources.getString(R.string.about_plaid_3), plaidDescription, null)); about3.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER), 0, about3.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); CharSequence desc = TextUtils.concat(about0, "\n\n", about1, "\n", about2, "\n\n", about3); HtmlUtils.setTextWithNiceLinks(plaidDescription, desc); } return aboutPlaid; case 1: if (aboutIcon == null) { aboutIcon = layoutInflater.inflate(R.layout.about_icon, parent, false); ButterKnife.bind(this, aboutIcon); CharSequence icon0 = resources.getString(R.string.about_icon_0); CharSequence icon1 = markdown.markdownToSpannable(resources .getString(R.string.about_icon_1), iconDescription, null); CharSequence iconDesc = TextUtils.concat(icon0, "\n", icon1); HtmlUtils.setTextWithNiceLinks(iconDescription, iconDesc); } return aboutIcon; case 2: if (aboutLibs == null) { aboutLibs = layoutInflater.inflate(R.layout.about_libs, parent, false); ButterKnife.bind(this, aboutLibs); libsList.setAdapter(new LibraryAdapter(host)); } return aboutLibs; } throw new InvalidParameterException(); } } private static class LibraryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private static final int VIEW_TYPE_INTRO = 0; private static final int VIEW_TYPE_LIBRARY = 1; static final Library[] libs = { new Library("Android support libraries", "The Android support libraries offer a number of features that are not built into the framework.", "https://developer.android.com/topic/libraries/support-library", "https://developer.android.com/images/android_icon_125.png", false), new Library("ButterKnife", "Bind Android views and callbacks to fields and methods.", "http://jakewharton.github.io/butterknife/", "https://avatars.githubusercontent.com/u/66577", true), new Library("Bypass", "Skip the HTML, Bypass takes markdown and renders it directly.", "https://github.com/Uncodin/bypass", "https://avatars.githubusercontent.com/u/1072254", true), new Library("Glide", "An image loading and caching library for Android focused on smooth scrolling.", "https://github.com/bumptech/glide", "https://avatars.githubusercontent.com/u/423539", false), new Library("JSoup", "Java HTML Parser, with best of DOM, CSS, and jquery.", "https://github.com/jhy/jsoup/", "https://avatars.githubusercontent.com/u/76934", true), new Library("OkHttp", "An HTTP & HTTP/2 client for Android and Java applications.", "http://square.github.io/okhttp/", "https://avatars.githubusercontent.com/u/82592", false), new Library("Retrofit", "A type-safe HTTP client for Android and Java.", "http://square.github.io/retrofit/", "https://avatars.githubusercontent.com/u/82592", false) }; private final CircleTransform circleCrop; final Activity host; LibraryAdapter(Activity host) { this.host = host; circleCrop = new CircleTransform(host); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { switch (viewType) { case VIEW_TYPE_INTRO: return new LibraryIntroHolder(LayoutInflater.from(parent.getContext()) .inflate(R.layout.about_lib_intro, parent, false)); case VIEW_TYPE_LIBRARY: return createLibraryHolder(parent); } throw new InvalidParameterException(); } private @NonNull LibraryHolder createLibraryHolder(ViewGroup parent) { final LibraryHolder holder = new LibraryHolder(LayoutInflater.from(parent.getContext()) .inflate(R.layout.library, parent, false)); View.OnClickListener clickListener = new View.OnClickListener() { @Override public void onClick(View v) { int position = holder.getAdapterPosition(); if (position == RecyclerView.NO_POSITION) return; CustomTabActivityHelper.openCustomTab( host, new CustomTabsIntent.Builder() .setToolbarColor(ContextCompat.getColor(host, R.color.primary)) .addDefaultShareMenuItem() .build(), Uri.parse(libs[position - 1].link)); } }; holder.itemView.setOnClickListener(clickListener); holder.link.setOnClickListener(clickListener); return holder; } @Override public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) { if (getItemViewType(position) == VIEW_TYPE_LIBRARY) { bindLibrary((LibraryHolder) holder, libs[position - 1]); // adjust for intro } } @Override public int getItemViewType(int position) { return position == 0 ? VIEW_TYPE_INTRO : VIEW_TYPE_LIBRARY; } @Override public int getItemCount() { return libs.length + 1; // + 1 for the static intro view } private void bindLibrary(final LibraryHolder holder, final Library lib) { holder.name.setText(lib.name); holder.description.setText(lib.description); DrawableRequestBuilder<String> request = Glide.with(holder.image.getContext()) .load(lib.imageUrl) .placeholder(R.drawable.avatar_placeholder); if (lib.circleCrop) { request.transform(circleCrop); } request.into(holder.image); } } static class LibraryHolder extends RecyclerView.ViewHolder { @BindView(R.id.library_image) ImageView image; @BindView(R.id.library_name) TextView name; @BindView(R.id.library_description) TextView description; @BindView(R.id.library_link) Button link; LibraryHolder(View itemView) { super(itemView); ButterKnife.bind(this, itemView); } } static class LibraryIntroHolder extends RecyclerView.ViewHolder { TextView intro; LibraryIntroHolder(View itemView) { super(itemView); intro = (TextView) itemView; } } /** * Models an open source library we want to credit */ private static class Library { final String name; final String link; final String description; final String imageUrl; final boolean circleCrop; Library(String name, String description, String link, String imageUrl, boolean circleCrop) { this.name = name; this.description = description; this.link = link; this.imageUrl = imageUrl; this.circleCrop = circleCrop; } } }