content: /*
   * Copyright (c) 2014 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 com.example.android.tvleanback.ui;
  
  import android.Manifest;
  import android.app.Activity;
  import android.content.ActivityNotFoundException;
  import android.content.Context;
  import android.content.Intent;
  import android.content.pm.PackageManager;
  import android.database.Cursor;
  import android.os.Bundle;
  import android.os.Handler;
  import androidx.leanback.app.SearchSupportFragment;
  import androidx.leanback.widget.ArrayObjectAdapter;
  import androidx.leanback.widget.CursorObjectAdapter;
  import androidx.leanback.widget.HeaderItem;
  import androidx.leanback.widget.ImageCardView;
  import androidx.leanback.widget.ListRow;
  import androidx.leanback.widget.ListRowPresenter;
  import androidx.leanback.widget.ObjectAdapter;
  import androidx.leanback.widget.OnItemViewClickedListener;
  import androidx.leanback.widget.Presenter;
  import androidx.leanback.widget.Row;
  import androidx.leanback.widget.RowPresenter;
  import androidx.leanback.widget.SpeechRecognitionCallback;
  import androidx.core.app.ActivityOptionsCompat;
  import androidx.loader.app.LoaderManager;
  import androidx.loader.content.CursorLoader;
  import androidx.loader.content.Loader;
  import android.text.TextUtils;
  import android.util.Log;
  import android.widget.Toast;
  
  import com.example.android.tvleanback.BuildConfig;
  import com.example.android.tvleanback.R;
  import com.example.android.tvleanback.data.VideoContract;
  import com.example.android.tvleanback.model.Video;
  import com.example.android.tvleanback.model.VideoCursorMapper;
  import com.example.android.tvleanback.presenter.CardPresenter;
  
  /*
   * This class demonstrates how to do in-app search
   */
  public class SearchFragment extends SearchSupportFragment
          implements SearchSupportFragment.SearchResultProvider,
          LoaderManager.LoaderCallbacks<Cursor> {
      private static final String TAG = "SearchFragment";
      private static final boolean DEBUG = BuildConfig.DEBUG;
      private static final boolean FINISH_ON_RECOGNIZER_CANCELED = true;
      private static final int REQUEST_SPEECH = 0x00000010;
  
      private final Handler mHandler = new Handler();
      private ArrayObjectAdapter mRowsAdapter;
      private String mQuery;
      private final CursorObjectAdapter mVideoCursorAdapter =
              new CursorObjectAdapter(new CardPresenter());
  
      private int mSearchLoaderId = 1;
      private boolean mResultsFound = false;
  
      @Override
      public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
  
          mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
          mVideoCursorAdapter.setMapper(new VideoCursorMapper());
  
          setSearchResultProvider(this);
          setOnItemViewClickedListener(new ItemViewClickedListener());
          if (DEBUG) {
              Log.d(TAG, "User is initiating a search. Do we have RECORD_AUDIO permission? " +
                  hasPermission(Manifest.permission.RECORD_AUDIO));
          }
          if (!hasPermission(Manifest.permission.RECORD_AUDIO)) {
              if (DEBUG) {
                  Log.d(TAG, "Does not have RECORD_AUDIO, using SpeechRecognitionCallback");
              }
              // SpeechRecognitionCallback is not required and if not provided recognition will be
              // handled using internal speech recognizer, in which case you must have RECORD_AUDIO
              // permission
              setSpeechRecognitionCallback(new SpeechRecognitionCallback() {
                  @Override
                  public void recognizeSpeech() {
                      try {
                          startActivityForResult(getRecognizerIntent(), REQUEST_SPEECH);
                      } catch (ActivityNotFoundException e) {
                          Log.e(TAG, "Cannot find activity for speech recognizer", e);
                      }
                  }
              });
          } else if (DEBUG) {
              Log.d(TAG, "We DO have RECORD_AUDIO");
          }
      }
  
      @Override
      public void onPause() {
          mHandler.removeCallbacksAndMessages(null);
          super.onPause();
      }
  
      @Override
      public void onActivityResult(int requestCode, int resultCode, Intent data) {
          switch (requestCode) {
              case REQUEST_SPEECH:
                  switch (resultCode) {
                      case Activity.RESULT_OK:
                          setSearchQuery(data, true);
                          break;
                      default:
                          // If recognizer is canceled or failed, keep focus on the search orb
                          if (FINISH_ON_RECOGNIZER_CANCELED) {
                              if (!hasResults()) {
                                  if (DEBUG) Log.v(TAG, "Voice search canceled");
                                  getView().findViewById(R.id.lb_search_bar_speech_orb).requestFocus();
                              }
                          }
                          break;
                  }
                  break;
          }
      }
  
      @Override
      public ObjectAdapter getResultsAdapter() {
          return mRowsAdapter;
      }
  
      @Override
      public boolean onQueryTextChange(String newQuery) {
          if (DEBUG) Log.i(TAG, String.format("Search text changed: %s", newQuery));
          loadQuery(newQuery);
          return true;
      }
  
      @Override
      public boolean onQueryTextSubmit(String query) {
          if (DEBUG) Log.i(TAG, String.format("Search text submitted: %s", query));
          loadQuery(query);
          return true;
      }
  
      public boolean hasResults() {
          return mRowsAdapter.size() > 0 && mResultsFound;
      }
  
      private boolean hasPermission(final String permission) {
          final Context context = getActivity();
          return PackageManager.PERMISSION_GRANTED == context.getPackageManager().checkPermission(
                  permission, context.getPackageName());
      }
  
      private void loadQuery(String query) {
          if (!TextUtils.isEmpty(query) && !query.equals("nil")) {
              mQuery = query;
              getLoaderManager().initLoader(mSearchLoaderId++, null, this);
          }
      }
  
      public void focusOnSearch() {
          getView().findViewById(R.id.lb_search_bar).requestFocus();
      }
  
      @Override
      public Loader<Cursor> onCreateLoader(int id, Bundle args) {
          String query = mQuery;
          return new CursorLoader(
                  getActivity(),
                  VideoContract.VideoEntry.CONTENT_URI,
                  null, // Return all fields.
                  VideoContract.VideoEntry.COLUMN_NAME + " LIKE ? OR " +
                          VideoContract.VideoEntry.COLUMN_DESC + " LIKE ?",
                  new String[]{"%" + query + "%", "%" + query + "%"},
                  null // Default sort order
          );
      }
  
      @Override
      public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
          int titleRes;
          if (cursor != null && cursor.moveToFirst()) {
              mResultsFound = true;
              titleRes = R.string.search_results;
          } else {
              mResultsFound = false;
              titleRes = R.string.no_search_results;
          }
          mVideoCursorAdapter.changeCursor(cursor);
          HeaderItem header = new HeaderItem(getString(titleRes, mQuery));
          mRowsAdapter.clear();
          ListRow row = new ListRow(header, mVideoCursorAdapter);
          mRowsAdapter.add(row);
      }
  
      @Override
      public void onLoaderReset(Loader<Cursor> loader) {
          mVideoCursorAdapter.changeCursor(null);
      }
  
      private final class ItemViewClickedListener implements OnItemViewClickedListener {
          @Override
          public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
                  RowPresenter.ViewHolder rowViewHolder, Row row) {
  
              if (item instanceof Video) {
                  Video video = (Video) item;
                  Intent intent = new Intent(getActivity(), VideoDetailsActivity.class);
                  intent.putExtra(VideoDetailsActivity.VIDEO, video);
  
                  Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(
                          getActivity(),
                          ((ImageCardView) itemViewHolder.view).getMainImageView(),
                          VideoDetailsActivity.SHARED_ELEMENT_NAME).toBundle();
                  getActivity().startActivity(intent, bundle);
              } else {
                  Toast.makeText(getActivity(), ((String) item), Toast.LENGTH_SHORT).show();
              }
          }
      }
  
  }
  