/* * Copyright 2016 Gleb Godonoga. * * 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 com.andrada.sitracker.ui; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.SearchManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.Bundle; import android.support.v4.app.ActivityCompat; import android.support.v4.content.IntentCompat; import android.support.v4.view.MenuItemCompat; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewAnimationUtils; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.animation.AnimationUtils; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.SearchView; import android.widget.Spinner; import com.andrada.sitracker.Constants; import com.andrada.sitracker.R; import com.andrada.sitracker.analytics.AnalyticsManager; import com.andrada.sitracker.contracts.AppUriContract; import com.andrada.sitracker.ui.fragment.RemoteAuthorsFragment; import com.github.amlcurran.showcaseview.ShowcaseView; import com.github.amlcurran.showcaseview.targets.ViewTarget; import org.androidannotations.annotations.AfterViews; import org.androidannotations.annotations.EActivity; import org.androidannotations.annotations.FragmentById; import org.androidannotations.annotations.InstanceState; import org.androidannotations.annotations.OptionsMenu; import java.util.ArrayList; import java.util.List; import static com.andrada.sitracker.util.LogUtils.LOGD; import static com.andrada.sitracker.util.LogUtils.LOGW; import static com.andrada.sitracker.util.LogUtils.makeLogTag; @SuppressLint("Registered") @EActivity(R.layout.activity_search) @OptionsMenu(R.menu.search_menu) public class SearchActivity extends BaseActivity { private static final String TAG = makeLogTag(SearchActivity.class); @FragmentById(R.id.remote_authors_fragment) RemoteAuthorsFragment mAuthorsFragment; SearchView mSearchView = null; @InstanceState String mQuery = ""; @InstanceState int mCurrentSearchType = 0; @AfterViews protected void afterViews() { super.afterViews(); Toolbar toolbar = getActionBarToolbar(); toolbar.setTitle(R.string.title_search); toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { dismiss(true); } }); String query = getIntent().getStringExtra(SearchManager.QUERY); if (query == null && mQuery != null) { query = mQuery; } mQuery = query; if (mSearchView != null) { mSearchView.setQuery(query, false); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { doEnterAnim(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); overridePendingTransition(0, 0); } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); populateSearchVariants(); } @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getSupportActionBar().setDisplayHomeAsUpEnabled(true); final MenuItem searchItem = menu.findItem(R.id.action_search); if (searchItem != null) { SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); final SearchView view = (SearchView) MenuItemCompat.getActionView(searchItem); mSearchView = view; if (view == null) { LOGW(TAG, "Could not set up search view, view is null."); } else { view.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); view.setIconified(false); view.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String s) { view.clearFocus(); if (mAuthorsFragment != null) { mAuthorsFragment.requestQueryUpdate(s, mCurrentSearchType); } return true; } @Override public boolean onQueryTextChange(String s) { mQuery = s; return true; } }); view.setOnCloseListener(new SearchView.OnCloseListener() { @Override public boolean onClose() { dismiss(false); return false; } }); new ShowcaseView.Builder(this) .setTarget(new ViewTarget(mSearchView)) .setContentTitle(getString(R.string.showcase_search_title)) .setContentText(getString(R.string.showcase_search_detail)) .setStyle(R.style.ShowcaseView_Base) .singleShot(Constants.SHOWCASE_ADD_AUTHORS_SEARCH_SHOT_ID).build(); if (!TextUtils.isEmpty(mQuery)) { view.setQuery(mQuery, false); } } } return true; } @Override public void onBackPressed() { dismiss(true); } public void dismiss(boolean navigateUp) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { doExitAnim(navigateUp); } else { ActivityCompat.finishAfterTransition(this); } } /** * On Lollipop+ perform a circular reveal animation (an expanding circular mask) when showing * the search panel. */ @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void doEnterAnim() { // Fade in a background scrim as this is a floating window. We could have used a // translucent window background but this approach allows us to turn off window animation & // overlap the fade with the reveal animation – making it feel snappier. View scrim = findViewById(R.id.scrim); scrim.animate() .alpha(1f) .setDuration(500L) .setInterpolator( AnimationUtils.loadInterpolator(this, android.R.interpolator.fast_out_slow_in)) .start(); // Next perform the circular reveal on the search panel final View searchPanel = findViewById(R.id.fragment_container); if (searchPanel != null) { // We use a view tree observer to set this up once the view is measured & laid out searchPanel.getViewTreeObserver().addOnPreDrawListener( new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { searchPanel.getViewTreeObserver().removeOnPreDrawListener(this); // As the height will change once the initial suggestions are delivered by the // loader, we can't use the search panels height to calculate the final radius // so we fall back to it's parent to be safe int revealRadius = ((ViewGroup) searchPanel.getParent()).getHeight(); // Center the animation on the top right of the panel i.e. near to the // search button which launched this screen. Animator show = ViewAnimationUtils.createCircularReveal(searchPanel, searchPanel.getRight(), searchPanel.getTop(), 0f, revealRadius); show.setDuration(250L); show.setInterpolator(AnimationUtils.loadInterpolator(SearchActivity.this, android.R.interpolator.fast_out_slow_in)); show.start(); return false; } }); } } /** * On Lollipop+ perform a circular animation (a contracting circular mask) when hiding the * search panel. */ @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void doExitAnim(final boolean navigateUp) { final View searchPanel = findViewById(R.id.fragment_container); // Center the animation on the top right of the panel i.e. near to the search button which // launched this screen. The starting radius therefore is the diagonal distance from the top // right to the bottom left int revealRadius = (int) Math.sqrt(Math.pow(searchPanel.getWidth(), 2) + Math.pow(searchPanel.getHeight(), 2)); // Animating the radius to 0 produces the contracting effect Animator shrink = ViewAnimationUtils.createCircularReveal(searchPanel, searchPanel.getRight(), searchPanel.getTop(), revealRadius, 0f); shrink.setDuration(200L); shrink.setInterpolator(AnimationUtils.loadInterpolator(SearchActivity.this, android.R.interpolator.fast_out_slow_in)); shrink.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { searchPanel.setVisibility(View.INVISIBLE); if (navigateUp) { navigateUpToFromChild(SearchActivity.this, IntentCompat.makeMainActivity(new ComponentName(SearchActivity.this, SiMainActivity_.class))); } else { ActivityCompat.finishAfterTransition(SearchActivity.this); } } }); shrink.start(); // We also animate out the translucent background at the same time. findViewById(R.id.scrim).animate() .alpha(0f) .setDuration(200L) .setInterpolator( AnimationUtils.loadInterpolator(SearchActivity.this, android.R.interpolator.fast_out_slow_in)) .start(); } @Override protected void onNewIntent(Intent intent) { LOGD(TAG, "SearchActivity.onNewIntent: " + intent); setIntent(intent); String query = intent.getStringExtra(SearchManager.QUERY); Bundle args = intentToFragmentArguments( new Intent(Intent.ACTION_VIEW, AppUriContract.buildSamlibSearchUri(query, 0))); if (mAuthorsFragment != null) { mAuthorsFragment.reloadFromArguments(args); } } private void populateSearchVariants() { Spinner searchOptionSpinner = (Spinner) findViewById(R.id.search_option_spinner); if (searchOptionSpinner != null) { List<String> items = new ArrayList<String>(); items.add(getString(R.string.search_type_name)); items.add(getString(R.string.search_type_keyword)); ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.search_spinner_item, android.R.id.text1, items); adapter.setDropDownViewResource(R.layout.search_spinner_item_dropdown); searchOptionSpinner.setAdapter(adapter); searchOptionSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) { if (position >= 0 && position < 2) { onSearchTypeSelected(position); } } @Override public void onNothingSelected(AdapterView<?> adapterView) { } }); if (mCurrentSearchType > 0) { searchOptionSpinner.setSelection(mCurrentSearchType); } } } private void onSearchTypeSelected(int position) { if (mCurrentSearchType == position) { return; } mCurrentSearchType = position; if (mAuthorsFragment != null && !TextUtils.isEmpty(mQuery)) { if (mSearchView != null) { mSearchView.clearFocus(); } mAuthorsFragment.requestQueryUpdate(mQuery, mCurrentSearchType); } } }