11abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/*
21abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Copyright (C) 2015 The Android Open Source Project
31abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
41abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License");
51abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * you may not use this file except in compliance with the License.
61abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * You may obtain a copy of the License at
71abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
81abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *      http://www.apache.org/licenses/LICENSE-2.0
91abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Unless required by applicable law or agreed to in writing, software
111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS,
121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * See the License for the specific language governing permissions and
141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * limitations under the License.
151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */
161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkopackage com.android.tv.tuner.setup;
181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
191abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.animation.LayoutTransition;
201abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.app.Activity;
211abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.app.ProgressDialog;
221abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.content.Context;
231abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.os.AsyncTask;
246ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport android.os.Build;
251abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.os.Bundle;
261abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.os.ConditionVariable;
271abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.os.Handler;
281abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.Log;
291abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.view.LayoutInflater;
301abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.view.View;
31ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.view.View.OnClickListener;
321abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.view.ViewGroup;
331abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.widget.BaseAdapter;
341abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.widget.Button;
351abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.widget.ListView;
361abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.widget.ProgressBar;
371abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.widget.TextView;
381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.common.SoftPreconditions;
40ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport com.android.tv.common.ui.setup.SetupFragment;
4165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.ChannelScanFileParser;
42721bd0da688cd552737fbb753a00597f95103b95Adrian Roosimport com.android.tv.tuner.R;
436ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.tuner.TunerHal;
4465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.TunerPreferences;
4565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData;
4665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.TunerChannel;
476ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.tuner.data.nano.Channel;
4865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.source.FileTsStreamer;
49d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport com.android.tv.tuner.source.TsDataSource;
5065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.source.TsStreamer;
5165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.source.TunerTsStreamer;
5265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.tvinput.ChannelDataManager;
5365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.tvinput.EventDetector;
541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
551abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.ArrayList;
561abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.List;
5765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.Locale;
5865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.concurrent.CountDownLatch;
5965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.concurrent.TimeUnit;
601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/**
62ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * A fragment for scanning channels.
631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */
64ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkopublic class ScanFragment extends SetupFragment {
65ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    private static final String TAG = "ScanFragment";
661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final boolean DEBUG = false;
676ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
68ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    // In the fake mode, the connection to antenna or cable is not necessary.
69ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    // Instead dummy channels are added.
70ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    private static final boolean FAKE_MODE = false;
71ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
7265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private static final String VCTLESS_CHANNEL_NAME_FORMAT = "RF%d-%d";
7365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
7465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public static final String ACTION_CATEGORY = "com.android.tv.tuner.setup.ScanFragment";
75ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    public static final int ACTION_CANCEL = 1;
76ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    public static final int ACTION_FINISH = 2;
77ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
78ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    public static final String EXTRA_FOR_CHANNEL_SCAN_FILE = "scan_file_choice";
791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final long CHANNEL_SCAN_SHOW_DELAY_MS = 10000;
811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final long CHANNEL_SCAN_PERIOD_MS = 4000;
82ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    private static final long SHOW_PROGRESS_DIALOG_DELAY_MS = 300;
831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // Build channels out of the locally stored TS streams.
851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final boolean SCAN_LOCAL_STREAMS = true;
861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private ChannelDataManager mChannelDataManager;
881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private ChannelScanTask mChannelScanTask;
891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private ProgressBar mProgressBar;
901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private TextView mScanningMessage;
911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private View mChannelHolder;
921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private ChannelAdapter mAdapter;
931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private volatile boolean mChannelListVisible;
941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private Button mCancelButton;
951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
97ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    public View onCreateView(LayoutInflater inflater, ViewGroup container,
98ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            Bundle savedInstanceState) {
996ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (DEBUG) Log.d(TAG, "onCreateView");
100ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        View view = super.onCreateView(inflater, container, savedInstanceState);
101ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mChannelDataManager = new ChannelDataManager(getActivity());
102ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mChannelDataManager.checkDataVersion(getActivity());
1031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mAdapter = new ChannelAdapter();
104ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mProgressBar = (ProgressBar) view.findViewById(R.id.tune_progress);
105ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mScanningMessage = (TextView) view.findViewById(R.id.tune_description);
106ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        ListView channelList = (ListView) view.findViewById(R.id.channel_list);
107ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        channelList.setAdapter(mAdapter);
108ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        channelList.setOnItemClickListener(null);
109ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        ViewGroup progressHolder = (ViewGroup) view.findViewById(R.id.progress_holder);
1101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        LayoutTransition transition = new LayoutTransition();
1111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        transition.enableTransitionType(LayoutTransition.CHANGING);
1121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        progressHolder.setLayoutTransition(transition);
113ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mChannelHolder = view.findViewById(R.id.channel_holder);
114ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mCancelButton = (Button) view.findViewById(R.id.tune_cancel);
115ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mCancelButton.setOnClickListener(new OnClickListener() {
1161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            @Override
1171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            public void onClick(View v) {
118ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                finishScan(false);
1191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
1201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        });
121ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        Bundle args = getArguments();
1226ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        int tunerType = (args == null ? 0 : args.getInt(TunerSetupActivity.KEY_TUNER_TYPE, 0));
123d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        // TODO: Handle the case when the fragment is restored.
124ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        startScan(args == null ? 0 : args.getInt(EXTRA_FOR_CHANNEL_SCAN_FILE, 0));
12565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        TextView scanTitleView = (TextView) view.findViewById(R.id.tune_title);
1266ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        switch (tunerType) {
1276ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            case TunerHal.TUNER_TYPE_USB:
1286ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                scanTitleView.setText(R.string.ut_channel_scan);
1296ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                break;
1306ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            case TunerHal.TUNER_TYPE_NETWORK:
1316ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                scanTitleView.setText(R.string.nt_channel_scan);
1326ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                break;
1336ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            default:
1346ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                scanTitleView.setText(R.string.bt_channel_scan);
13565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
136ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        return view;
1371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
140ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    protected int getLayoutResourceId() {
141ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        return R.layout.ut_channel_scan;
1421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
144ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    @Override
145ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    protected int[] getParentIdsForDelay() {
146ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        return new int[] {R.id.progress_holder};
1471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
149ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    private void startScan(int channelMapId) {
150ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mChannelScanTask = new ChannelScanTask(channelMapId);
151ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mChannelScanTask.execute();
1521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
1556ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    public void onPause() {
1566ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        Log.d(TAG, "onPause");
157d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        if (mChannelScanTask != null) {
158d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            // Ensure scan task will stop.
1596ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            Log.w(TAG, "The activity went to the background. Stopping channel scan.");
160d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            mChannelScanTask.stopScan();
161d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        }
1626ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        super.onPause();
1631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
165ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    /**
166ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     * Finishes the current scan thread. This fragment will be popped after the scan thread ends.
167ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     *
168ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     * @param cancel a flag which indicates the scan is canceled or not.
169ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     */
170ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    public void finishScan(boolean cancel) {
1711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (mChannelScanTask != null) {
172ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            mChannelScanTask.cancelScan(cancel);
1731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // Notifies a user of waiting to finish the scanning process.
1751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            new Handler().postDelayed(new Runnable() {
1761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                @Override
1771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                public void run() {
1786ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    if (mChannelScanTask != null) {
1796ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                        mChannelScanTask.showFinishingProgressDialog();
1806ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    }
1811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
182ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            }, SHOW_PROGRESS_DIALOG_DELAY_MS);
1831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // Hides the cancel button.
185ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            mCancelButton.setEnabled(false);
1861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private class ChannelAdapter extends BaseAdapter {
1901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        private final ArrayList<TunerChannel> mChannels;
1911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        public ChannelAdapter() {
1931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mChannels = new ArrayList<>();
1941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        @Override
1971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        public boolean areAllItemsEnabled() {
1981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return false;
1991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        @Override
2021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        public boolean isEnabled(int pos) {
2031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return false;
2041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        @Override
2071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        public int getCount() {
2081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return mChannels.size();
2091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        @Override
2121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        public Object getItem(int pos) {
2131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return pos;
2141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        @Override
2171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        public long getItemId(int pos) {
2181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return pos;
2191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        @Override
2221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        public View getView(int position, View convertView, ViewGroup parent) {
2231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            final Context context = parent.getContext();
2241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (convertView == null) {
2261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                LayoutInflater inflater = (LayoutInflater) context
2271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
2281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                convertView = inflater.inflate(R.layout.ut_channel_list, parent, false);
2291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
2301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            TextView channelNum = (TextView) convertView.findViewById(R.id.channel_num);
2321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            channelNum.setText(mChannels.get(position).getDisplayNumber());
2331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            TextView channelName = (TextView) convertView.findViewById(R.id.channel_name);
2351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            channelName.setText(mChannels.get(position).getName());
2361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return convertView;
2371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        public void add(TunerChannel channel) {
2401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mChannels.add(channel);
2411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            notifyDataSetChanged();
2421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
2441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
245ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    private class ChannelScanTask extends AsyncTask<Void, Integer, Void>
246d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            implements EventDetector.EventListener, ChannelDataManager.ChannelScanListener {
2471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        private static final int MAX_PROGRESS = 100;
2481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
249ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        private final Activity mActivity;
2501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        private final int mChannelMapId;
25165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        private final TsStreamer mScanTsStreamer;
25265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        private final TsStreamer mFileTsStreamer;
2531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        private final ConditionVariable mConditionStopped;
2541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
25565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        private final List<ChannelScanFileParser.ScanChannel> mScanChannelList = new ArrayList<>();
256ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        private boolean mIsCanceled;
2571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        private boolean mIsFinished;
2581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        private ProgressDialog mFinishingProgressDialog;
25965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        private CountDownLatch mLatch;
2601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        public ChannelScanTask(int channelMapId) {
262ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            mActivity = getActivity();
2631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mChannelMapId = channelMapId;
26465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            if (FAKE_MODE) {
26565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                mScanTsStreamer = new FakeTsStreamer(this);
26665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            } else {
2676ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                TunerHal hal = ((TunerSetupActivity) mActivity).getTunerHal();
26865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                if (hal == null) {
26965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    throw new RuntimeException("Failed to open a DVB device");
27065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                }
27165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                mScanTsStreamer = new TunerTsStreamer(hal, this);
27265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
2736ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            mFileTsStreamer = SCAN_LOCAL_STREAMS ? new FileTsStreamer(this, mActivity) : null;
2741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mConditionStopped = new ConditionVariable();
275d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            mChannelDataManager.setChannelScanListener(this, new Handler());
2761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        private void maybeSetChannelListVisible() {
279ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            mActivity.runOnUiThread(new Runnable() {
2801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                @Override
2811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                public void run() {
2821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    int channelsFound = mAdapter.getCount();
2831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    if (!mChannelListVisible && channelsFound > 0) {
2841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        String format = getResources().getQuantityString(
2851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                R.plurals.ut_channel_scan_message, channelsFound, channelsFound);
2861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        mScanningMessage.setText(String.format(format, channelsFound));
2871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        mChannelHolder.setVisibility(View.VISIBLE);
2881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        mChannelListVisible = true;
2891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    }
2901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
2911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            });
2921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        private void addChannel(final TunerChannel channel) {
295ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            mActivity.runOnUiThread(new Runnable() {
2961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                @Override
2971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                public void run() {
2981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    mAdapter.add(channel);
2991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    if (mChannelListVisible) {
3001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        int channelsFound = mAdapter.getCount();
3011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        String format = getResources().getQuantityString(
3021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                R.plurals.ut_channel_scan_message, channelsFound, channelsFound);
3031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        mScanningMessage.setText(String.format(format, channelsFound));
3041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    }
3051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
3061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            });
3071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        @Override
3101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        protected Void doInBackground(Void... params) {
31165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            mScanChannelList.clear();
3121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (SCAN_LOCAL_STREAMS) {
31365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                FileTsStreamer.addLocalStreamFiles(mScanChannelList);
3141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
31565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            mScanChannelList.addAll(ChannelScanFileParser.parseScanFile(
31665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    getResources().openRawResource(mChannelMapId)));
3171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            scanChannels();
3181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
3191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        @Override
32265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        protected void onCancelled() {
32365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            SoftPreconditions.checkState(false, TAG, "call cancelScan instead of cancel");
32465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
32565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
32665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        @Override
3271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        protected void onProgressUpdate(Integer... values) {
3286ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
3296ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                mProgressBar.setProgress(values[0], true);
3306ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            } else {
3316ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                mProgressBar.setProgress(values[0]);
3326ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
3331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        private void stopScan() {
3366ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (mLatch != null) {
3376ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                mLatch.countDown();
3386ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
3391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mConditionStopped.open();
3401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
342ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        private void cancelScan(boolean cancel) {
343ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            mIsCanceled = cancel;
344ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            stopScan();
345ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
346ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
3471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        private void scanChannels() {
3481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (DEBUG) Log.i(TAG, "Channel scan starting");
3491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mChannelDataManager.notifyScanStarted();
3501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            long startMs = System.currentTimeMillis();
3521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int i = 1;
35365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            for (ChannelScanFileParser.ScanChannel scanChannel : mScanChannelList) {
3541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int frequency = scanChannel.frequency;
3551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                String modulation = scanChannel.modulation;
3561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.i(TAG, "Tuning to " + frequency + " " + modulation);
3571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
35865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                TsStreamer streamer = getStreamer(scanChannel.type);
3596ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                SoftPreconditions.checkNotNull(streamer);
3606ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                if (streamer != null && streamer.startStream(scanChannel)) {
36165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    mLatch = new CountDownLatch(1);
36265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    try {
36365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        mLatch.await(CHANNEL_SCAN_PERIOD_MS, TimeUnit.MILLISECONDS);
36465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    } catch (InterruptedException e) {
36565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        Log.e(TAG, "The current thread is interrupted during scanChannels(). " +
36665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                                "The TS stream is stopped earlier than expected.", e);
36765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    }
36865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    streamer.stopStream();
3691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
37065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    addChannelsWithoutVct(scanChannel);
3711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    if (System.currentTimeMillis() > startMs + CHANNEL_SCAN_SHOW_DELAY_MS
3721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            && !mChannelListVisible) {
373ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                        maybeSetChannelListVisible();
3741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    }
3751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
3761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (mConditionStopped.block(-1)) {
3771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    break;
3781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
3796ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                publishProgress(MAX_PROGRESS * i++ / mScanChannelList.size());
38065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
3811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mChannelDataManager.notifyScanCompleted();
3821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (!mConditionStopped.block(-1)) {
3831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                publishProgress(MAX_PROGRESS);
3841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
3851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (DEBUG) Log.i(TAG, "Channel scan ended");
3861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
38965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        private void addChannelsWithoutVct(ChannelScanFileParser.ScanChannel scanChannel) {
39065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            if (scanChannel.radioFrequencyNumber == null
39165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    || !(mScanTsStreamer instanceof TunerTsStreamer)) {
39265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                return;
39365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
39465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            for (TunerChannel tunerChannel
39565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    : ((TunerTsStreamer) mScanTsStreamer).getMalFormedChannels()) {
39665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                if ((tunerChannel.getVideoPid() != TunerChannel.INVALID_PID)
39765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        && (tunerChannel.getAudioPid() != TunerChannel.INVALID_PID)) {
39865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    tunerChannel.setFrequency(scanChannel.frequency);
39965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    tunerChannel.setModulation(scanChannel.modulation);
40065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    tunerChannel.setShortName(String.format(Locale.US, VCTLESS_CHANNEL_NAME_FORMAT,
40165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                            scanChannel.radioFrequencyNumber,
40265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                            tunerChannel.getProgramNumber()));
40365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    tunerChannel.setVirtualMajor(scanChannel.radioFrequencyNumber);
40465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    tunerChannel.setVirtualMinor(tunerChannel.getProgramNumber());
405d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    onChannelDetected(tunerChannel, true);
40665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                }
40765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
40865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
40965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
41065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        private TsStreamer getStreamer(int type) {
4111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            switch (type) {
4121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                case Channel.TYPE_TUNER:
41365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    return mScanTsStreamer;
4141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                case Channel.TYPE_FILE:
41565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    return mFileTsStreamer;
4161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                default:
4171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    return null;
4181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
4191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
4211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        @Override
422ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        public void onEventDetected(TunerChannel channel, List<PsipData.EitItem> items) {
4231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mChannelDataManager.notifyEventDetected(channel, items);
4241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
4261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        @Override
42765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        public void onChannelScanDone() {
42865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            if (mLatch != null) {
42965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                mLatch.countDown();
4301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
43165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
43265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
43365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        @Override
43465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        public void onChannelDetected(TunerChannel channel, boolean channelArrivedAtFirstTime) {
4351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (channelArrivedAtFirstTime) {
43665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                Log.i(TAG, "Found channel " + channel);
43765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
43865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            if (channelArrivedAtFirstTime && channel.hasAudio()) {
43965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                // Playbacks with video-only stream have not been tested yet.
44065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                // No video-only channel has been found.
4411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                addChannel(channel);
4426ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                mChannelDataManager.notifyChannelDetected(channel, channelArrivedAtFirstTime);
4431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
4441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
44665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        public void showFinishingProgressDialog() {
4471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // Show a progress dialog to wait for the scanning process if it's not done yet.
448ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            if (!mIsFinished && mFinishingProgressDialog == null) {
449ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                mFinishingProgressDialog = ProgressDialog.show(mActivity, "",
450ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                        getString(R.string.ut_setup_cancel), true, false);
451ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            }
452ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
453d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko
454d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        @Override
455d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        public void onChannelHandlingDone() {
456d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            mChannelDataManager.setCurrentVersion(mActivity);
457d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            mChannelDataManager.releaseSafely();
458d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            mIsFinished = true;
459d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            TunerPreferences.setScannedChannelCount(mActivity.getApplicationContext(),
460d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    mChannelDataManager.getScannedChannelCount());
4616ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            // Cancel a previously shown notification.
4626ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            TunerSetupActivity.cancelNotification(mActivity.getApplicationContext());
463d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            // Mark scan as done
464d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            TunerPreferences.setScanDone(mActivity.getApplicationContext());
465d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            // finishing will be done manually.
466d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            if (mFinishingProgressDialog != null) {
467d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                mFinishingProgressDialog.dismiss();
468d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            }
4696ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            // If the fragment is not resumed, the next fragment (scan result page) can't be
4706ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            // displayed. In that case, just close the activity.
4716ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (isResumed()) {
4726ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                onActionClick(ACTION_CATEGORY, mIsCanceled ? ACTION_CANCEL : ACTION_FINISH);
4736ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            } else if (getActivity() != null) {
4746ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                getActivity().finish();
4756ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
476d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            mChannelScanTask = null;
477d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        }
478ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    }
479ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
48065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private static class FakeTsStreamer implements TsStreamer {
481ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        private final EventDetector.EventListener mEventListener;
482ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        private int mProgramNumber = 0;
483ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
48465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        FakeTsStreamer(EventDetector.EventListener eventListener) {
485ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            mEventListener = eventListener;
486ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
487ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
488ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        @Override
48965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        public boolean startStream(ChannelScanFileParser.ScanChannel channel) {
490ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            if (++mProgramNumber % 2 == 1) {
49165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                return true;
4921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
493ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            final String displayNumber = Integer.toString(mProgramNumber);
494ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            final String name = "Channel-" + mProgramNumber;
49565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            mEventListener.onChannelDetected(new TunerChannel(mProgramNumber, new ArrayList<>()) {
496ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                @Override
497ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                public String getDisplayNumber() {
498ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                    return displayNumber;
499ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                }
500ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
501ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                @Override
502ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                public String getName() {
503ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                    return name;
504ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                }
505ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            }, true);
50665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return true;
507ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
508ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
509ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        @Override
51065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        public boolean startStream(TunerChannel channel) {
51165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return false;
512ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
513ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
514ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        @Override
51565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        public void stopStream() {
516ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
517ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
518ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        @Override
519d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        public TsDataSource createDataSource() {
52065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return null;
5211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
5221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
5231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko}
524