SmartTube/smartyoutubetv2/src/main/java/com/liskovsoft/smartyoutubetv2/tv/ui/search/SearchFragment.java

238 lines
8.9 KiB
Java

/*
* 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.liskovsoft.smartyoutubetv2.tv.ui.search;
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.liskovsoft.smartyoutubetv2.tv.BuildConfig;
import com.liskovsoft.smartyoutubetv2.tv.R;
import com.liskovsoft.smartyoutubetv2.tv.data.old.VideoContract;
import com.liskovsoft.smartyoutubetv2.common.mvp.models.Video;
import com.liskovsoft.smartyoutubetv2.tv.model.old.VideoCursorMapper;
import com.liskovsoft.smartyoutubetv2.tv.presenter.CardPresenter;
import com.liskovsoft.smartyoutubetv2.tv.ui.old.VideoDetailsActivity;
/*
* 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();
}
}
}
}