Kore/app/src/main/java/org/xbmc/kore/ui/sections/hosts/HostListFragment.java

404 lines
16 KiB
Java

/*
* Copyright 2015 Synced Synapse. 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 org.xbmc.kore.ui.sections.hosts;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import org.xbmc.kore.R;
import org.xbmc.kore.host.HostInfo;
import org.xbmc.kore.host.HostManager;
import org.xbmc.kore.jsonrpc.ApiCallback;
import org.xbmc.kore.jsonrpc.HostConnection;
import org.xbmc.kore.jsonrpc.method.JSONRPC;
import org.xbmc.kore.ui.sections.remote.RemoteActivity;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.UIUtils;
import java.util.ArrayList;
import java.util.List;
import butterknife.ButterKnife;
import butterknife.BindView;
import butterknife.OnClick;
import butterknife.Unbinder;
/**
* Fragment to manage the list oof registered hosts.
*/
public class HostListFragment extends Fragment {
private static final String TAG = LogUtils.makeLogTag(HostListFragment.class);
private ArrayList<HostInfoRow> hostInfoRows = new ArrayList<HostInfoRow>();
private HostListAdapter adapter = null;
private Context context;
private Unbinder unbinder;
private Handler callbackHandler = new Handler();
@BindView(R.id.list) GridView hostGridView;
@BindView(R.id.action_add_host) Button addHostButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
context = inflater.getContext();
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_host_list, container, false);
unbinder = ButterKnife.bind(this, root);
// Get the host list
// TODO: This is being done synchronously !!!
final HostManager hostManager = HostManager.getInstance(context);
ArrayList<HostInfo> hosts = hostManager.getHosts();
HostInfo currentHost = hostManager.getHostInfo();
int currentHostPosition = 0;
for (int i = 0; i < hosts.size(); i++) {
if ((currentHost != null) && (hosts.get(i).getId() == currentHost.getId())) {
currentHostPosition = i;
}
hostInfoRows.add(new HostInfoRow(hosts.get(i), HostInfoRow.HOST_STATUS_CONNECTING));
}
// Setup the adapter
hostGridView.setEmptyView(root.findViewById(android.R.id.empty));
adapter = new HostListAdapter(context, R.layout.grid_item_host, hostInfoRows);
hostGridView.setAdapter(adapter);
hostGridView.setItemChecked(currentHostPosition, true);
hostGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long itemId) {
HostInfoRow clickedHostRow = hostInfoRows.get(position);
// Set the clicked host active
hostManager.switchHost(clickedHostRow.hostInfo);
Intent intent = new Intent(context, RemoteActivity.class);
context.startActivity(intent);
// switch (clickedHostRow.status) {
// case HostInfoRow.HOST_STATUS_CONNECTING:
// // Jsut switch the host
// hostManager.switchHost(clickedHostRow.hostInfo);
// break;
// case HostInfoRow.HOST_STATUS_AVAILABLE:
// // Set the clicked host active
// hostManager.switchHost(clickedHostRow.hostInfo);
// Intent intent = new Intent(context, RemoteActivity.class)
// .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// context.startActivity(intent);
// break;
// case HostInfoRow.HOST_STATUS_UNAVAILABLE:
// hostManager.switchHost(clickedHostRow.hostInfo);
// // Check host status again
// clickedHostRow.status = HostInfoRow.HOST_STATUS_CONNECTING;
// adapter.notifyDataSetChanged();
// updateHostStatus(clickedHostRow);
// break;
// }
}
});
return root;
}
@Override
public void onActivityCreated (Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Indicate that this fragment would like to influence the set of actions in the action bar.
setHasOptionsMenu(true);
}
@Override
public void onResume() {
super.onResume();
// Launch check on each host
for (HostInfoRow hostInfoRow : hostInfoRows ) {
updateHostStatus(hostInfoRow);
}
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
/**
* Pings the host to checks it status.
* <br/>
* Depending on whether it gets a response or not, updates {@link HostInfoRow} status, and
* notifies the adapter of the change
*
* @param hostInfoRow Host to check
*/
private void updateHostStatus(final HostInfoRow hostInfoRow) {
final HostConnection hostConnection = new HostConnection(hostInfoRow.hostInfo);
JSONRPC.Ping ping = new JSONRPC.Ping();
ping.execute(hostConnection, new ApiCallback<String>() {
@Override
public void onSuccess(String result) {
hostInfoRow.status = HostInfoRow.HOST_STATUS_AVAILABLE;
hostConnection.disconnect();
if (adapter != null)
adapter.notifyDataSetChanged();
}
@Override
public void onError(int errorCode, String description) {
hostInfoRow.status = HostInfoRow.HOST_STATUS_UNAVAILABLE;
hostConnection.disconnect();
if (adapter != null)
adapter.notifyDataSetChanged();
}
}, callbackHandler);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.host_manager, menu);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_add_host:
startAddHostWizard();
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
@OnClick(R.id.action_add_host)
public void onAddHostClicked(View v) {
startAddHostWizard();
}
/**
* Starts add host activity
*/
public void startAddHostWizard() {
Intent launchIntent = new Intent(getActivity(), AddHostActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(launchIntent);
getActivity().overridePendingTransition(R.anim.activity_in, R.anim.activity_out);
}
/**
* Auxiliary class to represent a host row in the list view
*/
private class HostInfoRow {
public HostInfo hostInfo;
public static final int HOST_STATUS_CONNECTING = 0,
HOST_STATUS_AVAILABLE = 1,
HOST_STATUS_UNAVAILABLE = 2;
/** Host status */
public int status;
public HostInfoRow(HostInfo host, int status) {
this.hostInfo = host;
this.status = status;
}
}
private View.OnClickListener hostlistItemMenuClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
final HostInfo hostInfo = (HostInfo)v.getTag();
final PopupMenu popupMenu = new PopupMenu(getActivity(), v);
popupMenu.getMenuInflater().inflate(R.menu.hostlist_item, popupMenu.getMenu());
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_remove_host:
DialogFragment confirmDelete = ConfirmDeleteDialogFragment
.getInstance(getDeleteDialogListener(hostInfo.getId()));
confirmDelete.show(getFragmentManager(), "confirmdelete");
return true;
case R.id.action_edit_host:
Intent launchIntent = new Intent(getActivity(), EditHostActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
.putExtra(HostFragmentManualConfiguration.HOST_ID, hostInfo.getId());
startActivity(launchIntent);
getActivity().overridePendingTransition(R.anim.activity_in, R.anim.activity_out);
return true;
case R.id.action_wake_up:
// Send WoL magic packet on a new thread
UIUtils.sendWolAsync(getActivity(), hostInfo);
return true;
}
return false;
}
});
popupMenu.show();
}
};
/**
* Adapter used to show the hosts in the {@link GridView}
*/
private class HostListAdapter extends ArrayAdapter<HostInfoRow> {
public HostListAdapter(Context context, int resource, List<HostInfoRow> objects) {
super(context, resource, objects);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(getActivity())
.inflate(R.layout.grid_item_host, parent, false);
}
final HostInfoRow item = this.getItem(position);
((TextView)convertView.findViewById(R.id.host_name)).setText(item.hostInfo.getName());
String hostAddress = item.hostInfo.getAddress() + ":" + item.hostInfo.getHttpPort();
((TextView)convertView.findViewById(R.id.host_address)).setText(hostAddress);
ImageView statusIndicator = (ImageView)convertView.findViewById(R.id.status_indicator);
// TODO: Change this colors to depend on the current theme
// int statusText;
int statusColor;
switch (item.status) {
case HostInfoRow.HOST_STATUS_CONNECTING:
statusColor = getActivity().getResources().getColor(R.color.host_status_connecting);
// statusText = R.string.connecting_to_xbmc;
break;
case HostInfoRow.HOST_STATUS_UNAVAILABLE:
statusColor = getActivity().getResources().getColor(R.color.host_status_unavailable);
// statusText = item.isCurrentHost? R.string.unable_to_connect_to_xbmc : R.string.xbmc_unavailable;
break;
case HostInfoRow.HOST_STATUS_AVAILABLE:
statusColor = getActivity().getResources().getColor(R.color.host_status_available);
// statusText = item.isCurrentHost? R.string.connected_to_xbmc : R.string.xbmc_available;
break;
default:
throw new RuntimeException("Invalid host status");
}
statusIndicator.setColorFilter(statusColor);
// ((TextView)convertView.findViewById(R.id.status_text)).setText(statusText);
// For the popupmenu
ImageView contextMenu = (ImageView)convertView.findViewById(R.id.list_context_menu);
contextMenu.setTag(item.hostInfo);
contextMenu.setOnClickListener(hostlistItemMenuClickListener);
return convertView;
}
}
private ConfirmDeleteDialogFragment.ConfirmDeleteDialogListener getDeleteDialogListener(final int hostId) {
return new ConfirmDeleteDialogFragment.ConfirmDeleteDialogListener() {
@Override
public void onDialogPositiveClick() {
HostManager hostManager = HostManager.getInstance(getActivity());
hostManager.deleteHost(hostId);
for (int i = 0; i < hostInfoRows.size(); i++) {
if (hostInfoRows.get(i).hostInfo.getId() == hostId) {
hostInfoRows.remove(i);
break;
}
}
adapter.notifyDataSetChanged();
}
@Override
public void onDialogNegativeClick() {
// Do nothing
}
};
}
/**
* Confirm host delete fragment
*/
public static class ConfirmDeleteDialogFragment extends DialogFragment {
/** Interface for communication with the enclosing fragment */
public interface ConfirmDeleteDialogListener {
public void onDialogPositiveClick();
public void onDialogNegativeClick();
}
private ConfirmDeleteDialogListener mListener;
public static ConfirmDeleteDialogFragment getInstance(ConfirmDeleteDialogListener listener) {
ConfirmDeleteDialogFragment frag = new ConfirmDeleteDialogFragment();
frag.mListener = listener;
return frag;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.delete_xbmc)
.setMessage(R.string.delete_xbmc_confirm)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mListener.onDialogPositiveClick();
}
})
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mListener.onDialogNegativeClick();
}
})
.create();
}
}
}