/* * Copyright (C) 2012 Google Inc. All rights reserved. * * 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.google.tv.anymotelibrary.connection; import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Color; import android.os.AsyncTask; import android.os.Bundle; import android.util.AttributeSet; import android.view.HapticFeedbackConstants; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import com.example.google.tv.anymotelibrary.R; import java.net.Inet4Address; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Used to present the discovered Google TV devices to the user. When user * selects one of the listed devices from this dialog, connection to the device * is initiated. */ public class DeviceSelectDialog extends Dialog { private DeviceListAdapter mDataAdapter; private DeviceSelectListener mListener; private TvDiscoveryService mTvDiscoveryService; private boolean mInitialized; private AsyncTask<Integer, Integer, List<TvDevice>> mDiscoveryTask; private ProgressBar progressBar; /** * Dialog listener */ public interface DeviceSelectListener { /** * Called whenever user picks up a new device to connect to. * * @param device - interface to remote device */ void onDeviceSelected(TvDevice device); /** * Called when device select dialog is dismissed */ void onDeviceSelectCancelled(); } /** * Constructor. * * @param context owner of the dialog. */ public DeviceSelectDialog(Context context) { super(context, android.R.style.Theme_Black_NoTitleBar_Fullscreen); } /* * events */ @Override protected void onStart() { super.onStart(); startDiscovery(); } @Override protected void onStop() { stopDiscovery(); super.onStop(); } @Override protected void onCreate(Bundle savedInstanceState) { ListView deviceView; setContentView(R.layout.device_select_layout); mDataAdapter = new DeviceListAdapter(); deviceView = (ListView) findViewById(R.id.dsl_stb_list); deviceView.setAdapter(mDataAdapter); deviceView.setOnItemClickListener(new AdapterView.OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View view, int position, long id) { TvDevice TvDevice = (TvDevice) parent.getItemAtPosition(position); selectDevice(TvDevice); } }); deviceView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { public boolean onItemLongClick( AdapterView<?> parent, View view, int position, long id) { TvDevice TvDevice = (TvDevice) parent.getItemAtPosition(position); view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); showDevice(TvDevice); return true; } }); findViewById(R.id.dsl_manual).setOnClickListener(new View.OnClickListener() { public void onClick(View v) { showManualIpDialog(); } }); findViewById(R.id.dsl_rescan).setOnClickListener(new View.OnClickListener() { public void onClick(View v) { startDiscovery(); } }); setOnCancelListener(new DialogInterface.OnCancelListener() { public void onCancel(DialogInterface dialog) { selectDevice(null); } }); progressBar = (ProgressBar) findViewById(R.id.a_progressbar); progressBar.setVisibility(View.VISIBLE); setCancelable(true); super.onCreate(savedInstanceState); mInitialized = true; } /** * Represents an entry in the box list. */ private static class ListEntryView extends LinearLayout { private Context myContext = null; private TvDevice listEntry = null; private TextView tvName = null; private TextView tvTargetAddr = null; public ListEntryView(Context context, AttributeSet attrs) { super(context, attrs); myContext = context; } public ListEntryView(Context context) { super(context); myContext = context; } @Override protected void onFinishInflate() { super.onFinishInflate(); tvName = (TextView) findViewById(R.id.device_select_item_name); tvTargetAddr = (TextView) findViewById(R.id.device_select_item_address); } private void updateContents() { if (null != tvName) { String txt = myContext.getString(R.string.unkown_tgt_name); if ((null != listEntry) && (null != listEntry.getName())) { txt = listEntry.getName(); } tvName.setText(txt); } if (null != tvTargetAddr) { String txt = myContext.getString(R.string.unkown_tgt_addr); if ((null != listEntry) && (null != listEntry.getLocation())) { txt = listEntry.getLocation(); } tvTargetAddr.setText(txt); } } public void setListEntry(TvDevice listEntry) { this.listEntry = listEntry; updateContents(); } } /** * Internal listview adapter. */ private class DeviceListAdapter extends BaseAdapter { private final List<TvDevice> trackedDevices; private TvDevice mCurrentDevice; public DeviceListAdapter() { trackedDevices = new ArrayList<TvDevice>(); setRecentDevices(null); } public int getCount() { return getTotalSize(); } @Override public boolean hasStableIds() { return false; } @Override public boolean areAllItemsEnabled() { return false; } @Override public boolean isEnabled(int position) { return (position != 0); } public Object getItem(int position) { return getTvDevice(position); } public long getItemId(int position) { return position; } public boolean add(TvDevice device) { if (!trackedDevices.contains(device)) { trackedDevices.add(device); Collections.sort(trackedDevices); notifyDataSetChanged(); return true; } return false; } public void setRecentDevices(TvDevice[] devices) { notifyDataSetChanged(); } public View getView(int position, View convertView, ViewGroup parent) { // /* // * Find an item and construct a view for it given the position. // * Layout / content is as follows: 0 : [ Recently Connected header // ] // * 1 : [ Recent Device #0 ] n : [ Recent Device #n-1 ] n+1: [ // * Discovered Devices header ] n+2: [ Discovered Device #0 ] n+m: // [ // * Discovered Device #m-2 ] // */ // if (position == 0) { // return getHeaderView(R.string.finder_recently_connected, // convertView); // } // // Skip Recently Connected header. // position -= 1; // // if (position < rememberedDevices.length) { // return getDeviceView(rememberedDevices[position], convertView, // true); // } // // Skip Recent Devices. // position -= rememberedDevices.length; if (position == 0) { return getHeaderView(R.string.finder_connect, convertView); } // Skip Discovered Devices header. position -= 1; if (position < trackedDevices.size()) { return getDeviceView(trackedDevices.get(position), convertView, false); } // Nothing more to skip, invalid index. return null; } /** * Construct titled separator for listview. * * @param resource string resource identifying title. * @param convertView currently presented view. * @return separator view. */ private View getHeaderView(int resource, View convertView) { View view = getLayoutInflater().inflate(R.layout.device_select_item_separator_layout, null); TextView text = (TextView) view.findViewById(R.id.header_text); text.setText(resource); return view; } /** * Construct view representing device. * * @param device element associated with the view. * @param convertView currently presented view. * @param isRecent specifies whether device has been recently connected. * @return device item. */ private View getDeviceView(TvDevice device, View convertView, boolean isRecent) { ListEntryView itemView; ImageView image; if (convertView == null || !(convertView instanceof ListEntryView)) { itemView = (ListEntryView) getLayoutInflater().inflate( R.layout.device_select_item_layout, null); } else { itemView = (ListEntryView) convertView; } image = (ImageView) itemView.findViewById(R.id.device_select_item_image); if (image != null) { if (device.equals(mCurrentDevice)) { image.setImageResource(android.R.drawable.ic_menu_upload_you_tube); } else if (isRecent) { image.setImageResource(android.R.drawable.ic_menu_recent_history); } else { image.setImageResource(android.R.drawable.ic_menu_search); } } if (device.equals(mCurrentDevice)) { itemView.setBackgroundColor(Color.argb(0x80, 0x00, 0x40, 0x20)); } else { itemView.setBackgroundColor(Color.TRANSPARENT); } itemView.setListEntry(device); return itemView; } private int getTotalSize() { // return sum of tracked devices and header. return trackedDevices.size() + 1; } private TvDevice getTvDevice(int position) { // Skip header. position--; if (position < 0) { return null; } // and check, if the new position fits into the second group. if (position < trackedDevices.size()) { return trackedDevices.get(position); } // bad luck. return null; } public void setCurrentDevice(TvDevice device) { mCurrentDevice = device; notifyDataSetChanged(); } } /** * Present dialog allowing manual IP address specification. */ private void showManualIpDialog() { progressBar.setVisibility(View.INVISIBLE); DeviceManualIPDialog ipDialog = new DeviceManualIPDialog(getContext()); ipDialog.setManualIPListener(new DeviceManualIPDialog.ManualIPListener() { public void onSelect(String name, Inet4Address address, int port) { selectDevice(new TvDevice(name, address, port)); } public void onCancel() { } }); ipDialog.show(); } private void startDiscovery() { if (!mInitialized || (mTvDiscoveryService == null) || (mDiscoveryTask != null)) { return; } // As TvDiscovery.discoverTvs method is blocking, launch an AsyncTask // to get a list of TVs. mDiscoveryTask = new AsyncTask<Integer, Integer, List<TvDevice>>() { /** * Start a discovery in background. */ @Override protected List<TvDevice> doInBackground(Integer... params) { return mTvDiscoveryService.discoverTvs(); } /** * Put results into the dialog, or pop a no-wifi dialog, after * discovery has finished. */ @Override protected void onPostExecute(List<TvDevice> tvs) { progressBar.setVisibility(View.INVISIBLE); if (tvs != null) { for (TvDevice tv : tvs) { mDataAdapter.add(tv); } } else { // For the time being, assume that null result means // wifi connection error, so show wifi config dialog. buildNoWifiDialog().show(); } mDiscoveryTask = null; } }; mDiscoveryTask.execute((Integer) null); } private void stopDiscovery() { if (mDiscoveryTask == null) { return; } mDiscoveryTask.cancel(true); mDiscoveryTask = null; } /** * Connect to specified device. * * @param device target device. */ private void selectDevice(TvDevice device) { Toast.makeText(getContext(), "Device selected", Toast.LENGTH_LONG); if (mListener != null) { if (device != null) { mListener.onDeviceSelected(device); } else { mListener.onDeviceSelectCancelled(); } } } /** * Construct and present a dialog describing device. * * @param device device to be presented to the user. */ private void showDevice(TvDevice device) { } /** * Construct a no-wifi dialog. * * @return AlertDialog asking user to turn on WIFI. */ private AlertDialog buildNoWifiDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); builder.setMessage(R.string.finder_wifi_not_available); builder.setCancelable(false); builder.setPositiveButton(R.string.finder_configure, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogInterface, int id) { Intent intent = new Intent(android.provider.Settings.ACTION_WIFI_SETTINGS); getContext().startActivity(intent); } }); builder.setNegativeButton(R.string.finder_cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogInterface, int id) { } }); return builder.create(); } /** * Set object receiving device select events. * * @param listener target object. */ public void setDeviceSelectListener(DeviceSelectListener listener) { this.mListener = listener; } /** * Set currently connected device to highlight it in the list. * * @param device currently connected device. */ public void setCurrentDevice(TvDevice device) { if (mInitialized) { mDataAdapter.setCurrentDevice(device); } } /** * Set list of recently connected TVs. * * @param devices list of recently connected devices. */ public void setRecentDevices(TvDevice[] devices) { if (mInitialized) { mDataAdapter.setRecentDevices(devices); } } /** * Set TV discoverer service. * * @param discovery service used to find neighboring TVs. */ public void setTvDiscovery(TvDiscoveryService discovery) { mTvDiscoveryService = discovery; startDiscovery(); } }