Java tutorial
/* * 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.app.ActivityManager; import android.app.ActivityOptions; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Typeface; import android.graphics.drawable.AnimatedVectorDrawable; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Bundle; import android.support.v4.content.ContextCompat; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; import android.text.style.ForegroundColorSpan; import android.text.style.ImageSpan; import android.text.style.StyleSpan; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewStub; import android.view.WindowInsets; import android.view.animation.AnimationUtils; import android.widget.ActionMenuView; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import android.widget.Toolbar; import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import butterknife.Bind; import butterknife.BindInt; import butterknife.ButterKnife; import butterknife.OnClick; import io.plaidapp.BuildConfig; import io.plaidapp.R; import io.plaidapp.data.DataManager; import io.plaidapp.data.PlaidItem; import io.plaidapp.data.Source; import io.plaidapp.data.api.ClientAuthInterceptor; import io.plaidapp.data.api.designernews.DesignerNewsService; import io.plaidapp.data.api.designernews.model.NewStoryRequest; import io.plaidapp.data.api.designernews.model.StoriesResponse; import io.plaidapp.data.pocket.PocketUtils; import io.plaidapp.data.prefs.DesignerNewsPrefs; import io.plaidapp.data.prefs.DribbblePrefs; import io.plaidapp.data.prefs.SourceManager; import io.plaidapp.ui.recyclerview.FilterTouchHelperCallback; import io.plaidapp.ui.recyclerview.GridItemDividerDecoration; import io.plaidapp.ui.recyclerview.InfiniteScrollListener; import io.plaidapp.ui.transitions.FabDialogMorphSetup; import io.plaidapp.util.ViewUtils; import retrofit.Callback; import retrofit.RestAdapter; import retrofit.RetrofitError; import retrofit.client.Response; public class HomeActivity extends Activity { private static final int RC_SEARCH = 0; private static final int RC_AUTH_DRIBBBLE_FOLLOWING = 1; private static final int RC_AUTH_DRIBBBLE_USER_LIKES = 2; private static final int RC_AUTH_DRIBBBLE_USER_SHOTS = 3; private static final int RC_NEW_DESIGNER_NEWS_STORY = 4; private static final int RC_NEW_DESIGNER_NEWS_LOGIN = 5; @Bind(R.id.drawer) DrawerLayout drawer; @Bind(R.id.toolbar) Toolbar toolbar; @Bind(R.id.stories_grid) RecyclerView grid; @Bind(R.id.fab) ImageButton fab; @Bind(R.id.filters) RecyclerView filtersList; @Bind(android.R.id.empty) ProgressBar loading; private TextView noFiltersEmptyText; private GridLayoutManager layoutManager; @BindInt(R.integer.num_columns) int columns; // data private DataManager dataManager; private FeedAdapter adapter; private FilterAdapter filtersAdapter; private DesignerNewsPrefs designerNewsPrefs; private DribbblePrefs dribbblePrefs; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); ButterKnife.bind(this); drawer.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); //toolbar.inflateMenu(R.menu.main); setActionBar(toolbar); if (savedInstanceState == null) { animateToolbar(); } dribbblePrefs = DribbblePrefs.get(this); designerNewsPrefs = DesignerNewsPrefs.get(this); filtersAdapter = new FilterAdapter(this, SourceManager.getSources(this), new FilterAdapter.FilterAuthoriser() { @Override public void requestDribbbleAuthorisation(View sharedElemeent, Source forSource) { Intent login = new Intent(HomeActivity.this, DribbbleLogin.class); login.putExtra(FabDialogMorphSetup.EXTRA_SHARED_ELEMENT_START_COLOR, ContextCompat.getColor(HomeActivity.this, R.color.background_dark)); ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(HomeActivity.this, sharedElemeent, getString(R.string.transition_dribbble_login)); startActivityForResult(login, getAuthSourceRequestCode(forSource), options.toBundle()); } }); dataManager = new DataManager(this, filtersAdapter) { @Override public void onDataLoaded(List<? extends PlaidItem> data) { adapter.addAndResort(data); checkEmptyState(); } }; adapter = new FeedAdapter(this, dataManager, columns, PocketUtils.isPocketInstalled(this)); grid.setAdapter(adapter); layoutManager = new GridLayoutManager(this, columns); layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { return adapter.getItemColumnSpan(position); } }); grid.setLayoutManager(layoutManager); grid.addOnScrollListener(gridScroll); grid.addOnScrollListener(new InfiniteScrollListener(layoutManager, dataManager) { @Override public void onLoadMore() { dataManager.loadAllDataSources(); } }); grid.setHasFixedSize(true); grid.addItemDecoration(new GridItemDividerDecoration(adapter.getDividedViewHolderClasses(), this, R.dimen.divider_height, R.color.divider)); grid.setItemAnimator(new HomeGridItemAnimator()); // drawer layout treats fitsSystemWindows specially so we have to handle insets ourselves drawer.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { @Override public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { // inset the toolbar down by the status bar height ViewGroup.MarginLayoutParams lpToolbar = (ViewGroup.MarginLayoutParams) toolbar.getLayoutParams(); lpToolbar.topMargin += insets.getSystemWindowInsetTop(); lpToolbar.rightMargin += insets.getSystemWindowInsetRight(); toolbar.setLayoutParams(lpToolbar); // inset the grid top by statusbar+toolbar & the bottom by the navbar (don't clip) grid.setPadding(grid.getPaddingLeft(), insets.getSystemWindowInsetTop() + ViewUtils.getActionBarSize(HomeActivity.this), grid.getPaddingRight() + insets.getSystemWindowInsetRight(), // landscape grid.getPaddingBottom()); // inset the fab for the navbar ViewGroup.MarginLayoutParams lpFab = (ViewGroup.MarginLayoutParams) fab.getLayoutParams(); lpFab.bottomMargin += insets.getSystemWindowInsetBottom(); // portrait lpFab.rightMargin += insets.getSystemWindowInsetRight(); // landscape fab.setLayoutParams(lpFab); // we place a background behind the status bar to combine with it's semi-transparent // color to get the desired appearance. Set it's height to the status bar height View statusBarBackground = findViewById(R.id.status_bar_background); FrameLayout.LayoutParams lpStatus = (FrameLayout.LayoutParams) statusBarBackground .getLayoutParams(); lpStatus.height = insets.getSystemWindowInsetTop(); statusBarBackground.setLayoutParams(lpStatus); // inset the filters list for the status bar / navbar // need to set the padding end for landscape case final boolean ltr = filtersList.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; filtersList.setPaddingRelative(filtersList.getPaddingStart(), filtersList.getPaddingTop() + insets.getSystemWindowInsetTop(), filtersList.getPaddingEnd() + (ltr ? insets.getSystemWindowInsetRight() : 0), filtersList.getPaddingBottom() + insets.getSystemWindowInsetBottom()); // clear this listener so insets aren't re-applied drawer.setOnApplyWindowInsetsListener(null); return insets.consumeSystemWindowInsets(); } }); setupTaskDescription(); filtersList.setAdapter(filtersAdapter); filtersAdapter.addFilterChangedListener(filtersChangedListener); filtersAdapter.addFilterChangedListener(dataManager); dataManager.loadAllDataSources(); ItemTouchHelper.Callback callback = new FilterTouchHelperCallback(filtersAdapter); ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback); itemTouchHelper.attachToRecyclerView(filtersList); checkEmptyState(); checkConnectivity(); } // listener for notifying adapter when data sources are deactivated private FilterAdapter.FiltersChangedListener filtersChangedListener = new FilterAdapter.FiltersChangedListener() { @Override public void onFiltersChanged(Source changedFilter) { if (!changedFilter.active) { adapter.removeDataSource(changedFilter.key); } checkEmptyState(); } @Override public void onFilterRemoved(Source removed) { adapter.removeDataSource(removed.key); checkEmptyState(); } }; private int gridScrollY = 0; private RecyclerView.OnScrollListener gridScroll = new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { gridScrollY += dy; if (gridScrollY > 0 && toolbar.getTranslationZ() != -1f) { toolbar.setTranslationZ(-1f); } else if (gridScrollY == 0 && toolbar.getTranslationZ() != 0) { toolbar.setTranslationZ(0f); } } }; @OnClick(R.id.fab) protected void fabClick() { if (designerNewsPrefs.isLoggedIn()) { Intent intent = new Intent(this, PostNewDesignerNewsStory.class); intent.putExtra(FabDialogMorphSetup.EXTRA_SHARED_ELEMENT_START_COLOR, ContextCompat.getColor(this, R.color.accent)); ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, fab, getString(R.string.transition_new_designer_news_post)); startActivityForResult(intent, RC_NEW_DESIGNER_NEWS_STORY, options.toBundle()); } else { Intent intent = new Intent(this, DesignerNewsLogin.class); intent.putExtra(FabDialogMorphSetup.EXTRA_SHARED_ELEMENT_START_COLOR, ContextCompat.getColor(this, R.color.accent)); ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, fab, getString(R.string.transition_designer_news_login)); startActivityForResult(intent, RC_NEW_DESIGNER_NEWS_LOGIN, options.toBundle()); } } private void checkEmptyState() { if (adapter.getDataItemCount() == 0) { // if grid is empty check whether we're loading or if no filters are selected if (filtersAdapter.getEnabledSourcesCount() > 0) { loading.setVisibility(View.VISIBLE); setNoFiltersEmptyTextVisibility(View.GONE); } else { loading.setVisibility(View.GONE); setNoFiltersEmptyTextVisibility(View.VISIBLE); } // ensure grid scroll tracking/toolbar z-order is reset gridScrollY = 0; toolbar.setTranslationZ(0f); } else { loading.setVisibility(View.GONE); setNoFiltersEmptyTextVisibility(View.GONE); } } private void setNoFiltersEmptyTextVisibility(int visibility) { if (visibility == View.VISIBLE) { if (noFiltersEmptyText == null) { // create the no filters empty text ViewStub stub = (ViewStub) findViewById(R.id.stub_no_filters); noFiltersEmptyText = (TextView) stub.inflate(); String emptyText = getString(R.string.no_filters_selected); int filterPlaceholderStart = emptyText.indexOf('\u08B4'); int altMethodStart = filterPlaceholderStart + 3; SpannableStringBuilder ssb = new SpannableStringBuilder(emptyText); // show an image of the filter icon ssb.setSpan(new ImageSpan(this, R.drawable.ic_filter_small, ImageSpan.ALIGN_BASELINE), filterPlaceholderStart, filterPlaceholderStart + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); // make the alt method (swipe from right) less prominent and italic ssb.setSpan(new ForegroundColorSpan(ContextCompat.getColor(this, R.color.text_secondary_light)), altMethodStart, emptyText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(new StyleSpan(Typeface.ITALIC), altMethodStart, emptyText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); noFiltersEmptyText.setText(ssb); noFiltersEmptyText.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { drawer.openDrawer(GravityCompat.END); } }); } noFiltersEmptyText.setVisibility(visibility); } else if (noFiltersEmptyText != null) { noFiltersEmptyText.setVisibility(visibility); } } private void setupTaskDescription() { // set a silhouette icon in overview as the launcher icon is a bit busy // and looks bad on top of colorPrimary //Bitmap overviewIcon = ImageUtils.vectorToBitmap(this, R.drawable.ic_launcher_silhouette); // TODO replace launcher icon with a monochrome version from RN. Bitmap overviewIcon = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); setTaskDescription(new ActivityManager.TaskDescription(getString(R.string.app_name), overviewIcon, ContextCompat.getColor(this, R.color.primary))); overviewIcon.recycle(); } @Override protected void onResume() { super.onResume(); dribbblePrefs.addLoginStatusListener(dataManager); dribbblePrefs.addLoginStatusListener(filtersAdapter); } @Override protected void onPause() { dribbblePrefs.removeLoginStatusListener(dataManager); dribbblePrefs.removeLoginStatusListener(filtersAdapter); super.onPause(); } private void animateToolbar() { // this is gross but toolbar doesn't expose it's children to animate them :( View t = toolbar.getChildAt(0); if (t != null && t instanceof TextView) { TextView title = (TextView) t; // fade in and space out the title. Animating the letterSpacing performs horribly so // fake it by setting the desired letterSpacing then animating the scaleX \_()_/ title.setAlpha(0f); title.setScaleX(0.8f); title.animate().alpha(1f).scaleX(1f).setStartDelay(300).setDuration(900).setInterpolator( AnimationUtils.loadInterpolator(this, android.R.interpolator.fast_out_slow_in)); } View amv = toolbar.getChildAt(1); if (amv != null & amv instanceof ActionMenuView) { ActionMenuView actions = (ActionMenuView) amv; popAnim(actions.getChildAt(0), 500, 200); // filter popAnim(actions.getChildAt(1), 700, 200); // overflow } } private void popAnim(View v, int startDelay, int duration) { if (v != null) { v.setAlpha(0f); v.setScaleX(0f); v.setScaleY(0f); v.animate().alpha(1f).scaleX(1f).scaleY(1f).setStartDelay(startDelay).setDuration(duration) .setInterpolator(AnimationUtils.loadInterpolator(this, android.R.interpolator.overshoot)); } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { MenuItem dribbbleLogin = menu.findItem(R.id.menu_dribbble_login); if (dribbbleLogin != null) { dribbbleLogin .setTitle(dribbblePrefs.isLoggedIn() ? R.string.dribbble_log_out : R.string.dribbble_login); } MenuItem designerNewsLogin = menu.findItem(R.id.menu_designer_news_login); if (designerNewsLogin != null) { designerNewsLogin.setTitle( designerNewsPrefs.isLoggedIn() ? R.string.designer_news_log_out : R.string.designer_news_login); } return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_filter: drawer.openDrawer(GravityCompat.END); return true; case R.id.menu_search: // get the icon's location on screen to pass through to the search screen View searchMenuView = toolbar.findViewById(R.id.menu_search); int[] loc = new int[2]; searchMenuView.getLocationOnScreen(loc); startActivityForResult( SearchActivity.createStartIntent(this, loc[0], loc[0] + (searchMenuView.getWidth() / 2)), RC_SEARCH, ActivityOptions.makeSceneTransitionAnimation(this).toBundle()); searchMenuView.setAlpha(0f); return true; case R.id.menu_dribbble_login: if (!dribbblePrefs.isLoggedIn()) { dribbblePrefs.login(HomeActivity.this); } else { dribbblePrefs.logout(); // TODO something better than a toast!! Toast.makeText(getApplicationContext(), R.string.dribbble_logged_out, Toast.LENGTH_SHORT).show(); } return true; case R.id.menu_designer_news_login: if (!designerNewsPrefs.isLoggedIn()) { startActivity(new Intent(this, DesignerNewsLogin.class)); } else { designerNewsPrefs.logout(); // TODO something better than a toast!! Toast.makeText(getApplicationContext(), R.string.designer_news_logged_out, Toast.LENGTH_SHORT) .show(); } return true; case R.id.menu_about: startActivity(new Intent(HomeActivity.this, AboutActivity.class), ActivityOptions.makeSceneTransitionAnimation(this).toBundle()); return true; } return super.onOptionsItemSelected(item); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case RC_SEARCH: // reset the search icon which we hid View searchMenuView = toolbar.findViewById(R.id.menu_search); if (searchMenuView != null) { searchMenuView.setAlpha(1f); } if (resultCode == SearchActivity.RESULT_CODE_SAVE) { String query = data.getStringExtra(SearchActivity.EXTRA_QUERY); if (TextUtils.isEmpty(query)) return; Source dribbbleSearch = null; Source designerNewsSearch = null; boolean newSource = false; if (data.getBooleanExtra(SearchActivity.EXTRA_SAVE_DRIBBBLE, false)) { dribbbleSearch = new Source.DribbbleSearchSource(query, true); newSource |= filtersAdapter.addFilter(dribbbleSearch); } if (data.getBooleanExtra(SearchActivity.EXTRA_SAVE_DESIGNER_NEWS, false)) { designerNewsSearch = new Source.DesignerNewsSearchSource(query, true); newSource |= filtersAdapter.addFilter(designerNewsSearch); } if (newSource && (dribbbleSearch != null || designerNewsSearch != null)) { highlightNewSources(dribbbleSearch, designerNewsSearch); } } break; case RC_NEW_DESIGNER_NEWS_STORY: if (resultCode == PostNewDesignerNewsStory.RESULT_DRAG_DISMISSED) { // need to reshow the FAB as there's no shared element transition showFab(); } else if (resultCode == PostNewDesignerNewsStory.RESULT_POST) { String title = data.getStringExtra(PostNewDesignerNewsStory.EXTRA_STORY_TITLE); String url = data.getStringExtra(PostNewDesignerNewsStory.EXTRA_STORY_URL); String comment = data.getStringExtra(PostNewDesignerNewsStory.EXTRA_STORY_COMMENT); if (!TextUtils.isEmpty(title)) { NewStoryRequest storyToPost = null; if (!TextUtils.isEmpty(url)) { storyToPost = NewStoryRequest.createWithUrl(title, url); } else if (!TextUtils.isEmpty(comment)) { storyToPost = NewStoryRequest.createWithComment(title, comment); } if (storyToPost != null) { // TODO: move this to a service in follow up CL? DesignerNewsService designerNewsApi = new RestAdapter.Builder() .setEndpoint(DesignerNewsService.ENDPOINT) .setRequestInterceptor(new ClientAuthInterceptor(designerNewsPrefs.getAccessToken(), BuildConfig.DESIGNER_NEWS_CLIENT_ID)) .build().create(DesignerNewsService.class); designerNewsApi.postStory(storyToPost, new Callback<StoriesResponse>() { @Override public void success(StoriesResponse story, Response response) { if (story != null && story.stories != null && story.stories.size() > 0) { long id = story.stories.get(0).id; } } @Override public void failure(RetrofitError error) { Log.e("HomeActivity", "Failed posting story", error); } }); } } } break; case RC_NEW_DESIGNER_NEWS_LOGIN: if (resultCode == RESULT_OK) { showFab(); } break; case RC_AUTH_DRIBBBLE_FOLLOWING: if (resultCode == RESULT_OK) { filtersAdapter.enableFilterByKey(SourceManager.SOURCE_DRIBBBLE_FOLLOWING, this); } break; case RC_AUTH_DRIBBBLE_USER_LIKES: if (resultCode == RESULT_OK) { filtersAdapter.enableFilterByKey(SourceManager.SOURCE_DRIBBBLE_USER_LIKES, this); } break; case RC_AUTH_DRIBBBLE_USER_SHOTS: if (resultCode == RESULT_OK) { filtersAdapter.enableFilterByKey(SourceManager.SOURCE_DRIBBBLE_USER_SHOTS, this); } break; } } private void showFab() { fab.setAlpha(0f); fab.setScaleX(0f); fab.setScaleY(0f); fab.setTranslationY(fab.getHeight() / 2); fab.animate().alpha(1f).scaleX(1f).scaleY(1f).translationY(0f).setDuration(300L) .setInterpolator(AnimationUtils.loadInterpolator(this, android.R.interpolator.linear_out_slow_in)) .start(); } /** * Highlight the new item by: * 1. opening the drawer * 2. scrolling it into view * 3. flashing it's background * 4. closing the drawer */ private void highlightNewSources(final Source... sources) { final Runnable closeDrawerRunnable = new Runnable() { @Override public void run() { drawer.closeDrawer(GravityCompat.END); } }; drawer.setDrawerListener(new DrawerLayout.SimpleDrawerListener() { // if the user interacts with the filters while it's open then don't auto-close private final View.OnTouchListener filtersTouch = new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { drawer.removeCallbacks(closeDrawerRunnable); return false; } }; @Override public void onDrawerOpened(View drawerView) { // scroll to the new item(s) and highlight them List<Integer> filterPositions = new ArrayList<>(sources.length); for (Source source : sources) { if (source != null) { filterPositions.add(filtersAdapter.getFilterPosition(source)); } } int scrollTo = Collections.max(filterPositions); filtersList.smoothScrollToPosition(scrollTo); for (int position : filterPositions) { FilterAdapter.FilterViewHolder holder = (FilterAdapter.FilterViewHolder) filtersList .findViewHolderForAdapterPosition(position); if (holder != null) { // this is failing for the first saved search, then working for subsequent calls // TODO work out why! holder.highlightFilter(); } } filtersList.setOnTouchListener(filtersTouch); } @Override public void onDrawerClosed(View drawerView) { // reset filtersList.setOnTouchListener(null); drawer.setDrawerListener(null); } @Override public void onDrawerStateChanged(int newState) { // if the user interacts with the drawer manually then don't auto-close if (newState == DrawerLayout.STATE_DRAGGING) { drawer.removeCallbacks(closeDrawerRunnable); } } }); drawer.openDrawer(GravityCompat.END); drawer.postDelayed(closeDrawerRunnable, 2000); } @Override public void onBackPressed() { if (drawer.isDrawerOpen(GravityCompat.END)) { drawer.closeDrawer(GravityCompat.END); } else { super.onBackPressed(); } } private void checkConnectivity() { ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService( Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); boolean connected = activeNetworkInfo != null && activeNetworkInfo.isConnected(); if (!connected) { loading.setVisibility(View.GONE); ViewStub stub = (ViewStub) findViewById(R.id.stub_no_connection); ImageView iv = (ImageView) stub.inflate(); final AnimatedVectorDrawable avd = (AnimatedVectorDrawable) getDrawable(R.drawable.avd_no_connection); iv.setImageDrawable(avd); avd.start(); } } private int getAuthSourceRequestCode(Source filter) { switch (filter.key) { case SourceManager.SOURCE_DRIBBBLE_FOLLOWING: return RC_AUTH_DRIBBBLE_FOLLOWING; case SourceManager.SOURCE_DRIBBBLE_USER_LIKES: return RC_AUTH_DRIBBBLE_USER_LIKES; case SourceManager.SOURCE_DRIBBBLE_USER_SHOTS: return RC_AUTH_DRIBBBLE_USER_SHOTS; } throw new InvalidParameterException(); } }