blob: 3a119940b648398b1e759bda7141652ffdbf6d48 [file] [log] [blame]
package com.chip.casting.app;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.R;
import com.chip.casting.DiscoveredNodeData;
import com.chip.casting.FailureCallback;
import com.chip.casting.MatterError;
import com.chip.casting.SuccessCallback;
import com.chip.casting.TvCastingApp;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/** A {@link Fragment} to discover commissioners on the network */
public class CommissionerDiscoveryFragment extends Fragment {
private static final String TAG = CommissionerDiscoveryFragment.class.getSimpleName();
private static final long DISCOVERY_POLL_INTERVAL_MS = 15000;
private static final List<DiscoveredNodeData> commissionerVideoPlayerList = new ArrayList<>();
private FailureCallback failureCallback;
private SuccessCallback<DiscoveredNodeData> successCallback;
private ScheduledFuture poller;
private final TvCastingApp tvCastingApp;
private ScheduledExecutorService executor;
public CommissionerDiscoveryFragment(TvCastingApp tvCastingApp) {
this.tvCastingApp = tvCastingApp;
this.executor = Executors.newSingleThreadScheduledExecutor();
}
/**
* Use this factory method to create a new instance of this fragment using the provided
* parameters.
*
* @return A new instance of fragment CommissionerDiscoveryFragment.
*/
public static CommissionerDiscoveryFragment newInstance(TvCastingApp tvCastingApp) {
return new CommissionerDiscoveryFragment(tvCastingApp);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_commissioner_discovery, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
Log.d(TAG, "onViewCreated() called");
super.onViewCreated(view, savedInstanceState);
Button manualCommissioningButton = getView().findViewById(R.id.manualCommissioningButton);
// In the ideal case we wouldn't rely on the host activity to maintain this object since
// the lifecycle of the context isn't tied to this callback. Since this is an example app
// this should work fine.
Callback callback = (Callback) this.getActivity();
View.OnClickListener manualCommissioningButtonOnClickListener =
v -> callback.handleCommissioningButtonClicked(null);
manualCommissioningButton.setOnClickListener(manualCommissioningButtonOnClickListener);
Button purgeCacheButton = getView().findViewById(R.id.purgeCacheButton);
Context context = getContext().getApplicationContext();
View.OnClickListener purgeCacheOnClickListener =
new View.OnClickListener() {
@Override
public void onClick(View v) {
boolean status = tvCastingApp.purgeCache();
Toast.makeText(
context,
"Cache purge " + (status ? "successful!" : "failed!"),
Toast.LENGTH_SHORT)
.show();
}
};
purgeCacheButton.setOnClickListener(purgeCacheOnClickListener);
ArrayAdapter<DiscoveredNodeData> arrayAdapter =
new VideoPlayerCommissionerAdapter(getActivity(), commissionerVideoPlayerList);
final ListView list = getActivity().findViewById(R.id.commissionerList);
list.setAdapter(arrayAdapter);
this.successCallback =
new SuccessCallback<DiscoveredNodeData>() {
@Override
public void handle(DiscoveredNodeData discoveredNodeData) {
Log.d(
TAG,
"SuccessCallback handle() Discovered a Video Player Commissioner: "
+ discoveredNodeData);
new Handler(Looper.getMainLooper())
.post(
() -> {
final Optional<DiscoveredNodeData> discoveredNodeInList =
commissionerVideoPlayerList
.stream()
.filter(node -> discoveredNodeData.discoveredNodeHasSameSource(node))
.findFirst();
if (discoveredNodeInList.isPresent()) {
Log.d(
TAG,
"Replacing existing entry "
+ discoveredNodeInList.get().getDeviceName()
+ " in players list");
arrayAdapter.remove(discoveredNodeInList.get());
}
arrayAdapter.add(discoveredNodeData);
});
}
};
this.failureCallback =
new FailureCallback() {
@Override
public void handle(MatterError matterError) {
Log.e(
TAG,
"FailureCallback handle() Error occurred during video player commissioner discovery: "
+ matterError);
if (MatterError.DISCOVERY_SERVICE_LOST == matterError) {
Log.d(TAG, "Attempting to restart service");
tvCastingApp.discoverVideoPlayerCommissioners(successCallback, this);
}
}
};
Button discoverButton = getView().findViewById(R.id.discoverButton);
discoverButton.setOnClickListener(
v -> {
Log.d(TAG, "Discovering on button click");
tvCastingApp.discoverVideoPlayerCommissioners(successCallback, failureCallback);
});
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume() called. Auto discovering");
poller =
executor.scheduleAtFixedRate(
() -> {
tvCastingApp.discoverVideoPlayerCommissioners(successCallback, failureCallback);
},
0,
DISCOVERY_POLL_INTERVAL_MS,
TimeUnit.MILLISECONDS);
}
@Override
public void onPause() {
super.onPause();
tvCastingApp.stopVideoPlayerDiscovery();
poller.cancel(true);
}
/** Interface for notifying the host. */
public interface Callback {
/** Notifies listener of Commissioning Button click. */
void handleCommissioningButtonClicked(DiscoveredNodeData selectedCommissioner);
}
}
class VideoPlayerCommissionerAdapter extends ArrayAdapter<DiscoveredNodeData> {
private final List<DiscoveredNodeData> playerList;
private final Context context;
private LayoutInflater inflater;
private static final String TAG = VideoPlayerCommissionerAdapter.class.getSimpleName();
public VideoPlayerCommissionerAdapter(Context context, List<DiscoveredNodeData> playerList) {
super(context, 0, playerList);
this.context = context;
this.playerList = playerList;
inflater = (LayoutInflater.from(context));
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
view = inflater.inflate(R.layout.commissionable_player_list_item, null);
String buttonText = getCommissionerButtonText(playerList.get(i));
Button playerDescription = view.findViewById(R.id.commissionable_player_description);
playerDescription.setText(buttonText);
View.OnClickListener clickListener =
v -> {
DiscoveredNodeData discoveredNodeData = playerList.get(i);
Log.d(TAG, "OnItemClickListener.onClick called for " + discoveredNodeData);
CommissionerDiscoveryFragment.Callback callback1 =
(CommissionerDiscoveryFragment.Callback) context;
callback1.handleCommissioningButtonClicked(discoveredNodeData);
};
playerDescription.setOnClickListener(clickListener);
return view;
}
private String getCommissionerButtonText(DiscoveredNodeData commissioner) {
String main = commissioner.getDeviceName() != null ? commissioner.getDeviceName() : "";
String aux =
"" + (commissioner.getProductId() > 0 ? "Product ID: " + commissioner.getProductId() : "");
aux +=
commissioner.getDeviceType() > 0
? (aux.isEmpty() ? "" : " ") + "Device Type: " + commissioner.getDeviceType()
: "";
aux +=
commissioner.getVendorId() > 0
? (aux.isEmpty() ? "" : " from ") + "Vendor ID: " + commissioner.getVendorId()
: "";
aux = aux.isEmpty() ? aux : "\n[" + aux + "]";
String preCommissioned = commissioner.isPreCommissioned() ? " (Pre-commissioned)" : "";
return main + aux + preCommissioned;
}
}