package org.deviceconnect.android.ui.activity; import android.app.ActionBar; import android.app.Activity; import android.app.AlertDialog; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.view.LayoutInflater; 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.CheckBox; import android.widget.CompoundButton; import android.widget.ListView; import android.widget.TextView; import org.deviceconnect.android.R; import org.deviceconnect.android.message.DConnectMessageService; import org.deviceconnect.android.service.DConnectService; import org.deviceconnect.android.service.DConnectServiceListener; import org.deviceconnect.android.service.DConnectServiceProvider; import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.logging.Logger; /** * Device Connect Service List Window. * * @author NTT DOCOMO, INC. */ public abstract class DConnectServiceListActivity extends FragmentActivity implements DConnectServiceListener { private DConnectMessageService mMessageService; private DConnectServiceProvider mProvider; private boolean mIsBound; private final Logger mLogger = Logger.getLogger("org.deviceconnect.dplugin"); private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(final ComponentName name, final IBinder service) { info("onServiceConnected: " + name); runOnUiThread(new Runnable() { @Override public void run() { mMessageService = ((DConnectMessageService.LocalBinder) service).getMessageService(); mProvider = mMessageService.getServiceProvider(); mProvider.addServiceListener(DConnectServiceListActivity.this); mIsBound = true; showServiceViewer(); } }); } @Override public void onServiceDisconnected(final ComponentName name) { info("onServiceDisconnected: " + name); mIsBound = false; mProvider.removeServiceListener(DConnectServiceListActivity.this); } }; private final OnClickServiceListener mOnClickServiceListener = new OnClickServiceListener() { @Override public void onClickService(final ServiceContainer service) { onItemClick(getService(service.getId())); } }; private Class<? extends Activity> mManualActivity; @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_service_list); ActionBar actionBar = getActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_HOME); actionBar.setTitle(R.string.activity_service_list_title); } mManualActivity = getSettingManualActivityClass(); bindMessageService(); } @Override protected void onDestroy() { super.onDestroy(); unbindMessageService(); } private void bindMessageService() { if (!mIsBound) { Intent intent = new Intent(getApplicationContext(), getMessageServiceClass()); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } } private void unbindMessageService() { if (mIsBound) { unbindService(mConnection); mIsBound = false; } } @Override public boolean onOptionsItemSelected(final MenuItem item) { if (item.getItemId() == android.R.id.home) { finish(); return true; } return super.onOptionsItemSelected(item); } protected boolean enablesItemClick() { return false; } protected void onItemClick(final DConnectService service) { } protected abstract Class<? extends DConnectMessageService> getMessageServiceClass(); protected abstract Class<? extends Activity> getSettingManualActivityClass(); protected void removeService(final String serviceId) { mProvider.removeService(serviceId); } protected DConnectService getService(final String serviceId) { return mProvider.getService(serviceId); } protected ListItemFilter getListItemFilter() { return null; } protected DConnectMessageService getMessageService() { return mMessageService; } private boolean doFilter(final DConnectService service) { ListItemFilter filter = getListItemFilter(); if (filter == null) { return true; } return filter.doFilter(service); } private List<ServiceContainer> getServiceContainers() { List<ServiceContainer> containers = new ArrayList<ServiceContainer>(); if (mProvider != null) { for (DConnectService entity : mProvider.getServiceList()) { if (doFilter(entity)) { containers.add(new ServiceContainer(entity)); } } } return containers; } private void showSettingActivity() { Intent intent = new Intent(getApplicationContext(), mManualActivity); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); } private void showServiceViewer() { FragmentManager mgr = getSupportFragmentManager(); FragmentTransaction transaction = mgr.beginTransaction(); ViewerFragment fragment = new ViewerFragment(); if (enablesItemClick()) { fragment.mOnClickServiceListener = mOnClickServiceListener; } transaction.replace(R.id.fragment_container, fragment); transaction.commit(); } private void showServiceRemover() { FragmentManager mgr = getSupportFragmentManager(); FragmentTransaction transaction = mgr.beginTransaction(); Fragment fragment = new RemoverFragment(); transaction.replace(R.id.fragment_container, fragment); transaction.commit(); } private void info(final String message) { mLogger.info(message); } @Override public void onServiceAdded(final DConnectService service) { if (!doFilter(service)) { return; } runOnUiThread(new Runnable() { @Override public void run() { FragmentManager mgr = getSupportFragmentManager(); List<Fragment> fragments = mgr.getFragments(); if (fragments != null) { for (Fragment f : fragments) { if (f instanceof AbstractViewerFragment) { ((AbstractViewerFragment) f).onServiceAdded(service); } } } } }); } @Override public void onServiceRemoved(final DConnectService service) { if (!doFilter(service)) { return; } runOnUiThread(new Runnable() { @Override public void run() { FragmentManager mgr = getSupportFragmentManager(); List<Fragment> fragments = mgr.getFragments(); if (fragments != null) { for (Fragment f : fragments) { if (f instanceof AbstractViewerFragment) { ((AbstractViewerFragment) f).onServiceRemoved(service); } } } } }); } @Override public void onStatusChange(final DConnectService service) { if (!doFilter(service)) { return; } runOnUiThread(new Runnable() { @Override public void run() { FragmentManager mgr = getSupportFragmentManager(); List<Fragment> fragments = mgr.getFragments(); if (fragments != null) { for (Fragment f : fragments) { if (f instanceof AbstractViewerFragment) { ((AbstractViewerFragment) f).onStatusChange(service); } } } } }); } private static class ServiceContainer { private final String mId; private String mName; private boolean mIsOnline; public ServiceContainer(final DConnectService service) { mId = service.getId(); setName(service.getName()); setOnline(service.isOnline()); } public String getId() { return mId; } public String getName() { return mName; } public void setName(final String name) { mName = name; } public void setOnline(final boolean online) { mIsOnline = online; } boolean isOnline() { return mIsOnline; } } public static abstract class AbstractViewerFragment extends Fragment { protected ListView mListView; protected View mNoServiceView; protected Button mRemoveServiceButton; protected ServiceListAdapter mListAdapter; protected DConnectServiceProvider mProvider; private final Object mLock = new Object(); protected List<ServiceContainer> getServiceContainers() { DConnectServiceListActivity activity = (DConnectServiceListActivity) getActivity(); if (activity == null) { return new ArrayList<ServiceContainer>(); } return activity.getServiceContainers(); } @Override public void onActivityCreated(final Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); DConnectServiceListActivity activity = (DConnectServiceListActivity) getActivity(); mProvider = activity.mProvider; } @Override public void onDestroyView() { super.onDestroyView(); } public void onServiceAdded(final DConnectService service) { ServiceContainer container = mListAdapter.getServiceContainer(service.getId()); if (container == null) { mListAdapter.add(new ServiceContainer(service)); } else { onStatusChange(service); } } public void onServiceRemoved(final DConnectService service) { synchronized (mLock) { ServiceContainer container = mListAdapter.getServiceContainer(service.getId()); if (container != null) { mListAdapter.remove(container); } toggleView(); } } public void onStatusChange(final DConnectService service) { synchronized (mLock) { ServiceContainer container = mListAdapter.getServiceContainer(service.getId()); if (container != null) { mListAdapter.onStatusChange(service); } toggleView(); } } protected void toggleView() { int serviceCount = mListAdapter.getCount(); if (serviceCount > 0) { mListView.setVisibility(View.VISIBLE); mNoServiceView.setVisibility(View.GONE); mRemoveServiceButton.setEnabled(true); } else { mListView.setVisibility(View.GONE); mNoServiceView.setVisibility(View.VISIBLE); mRemoveServiceButton.setEnabled(false); } } } public static class ViewerFragment extends AbstractViewerFragment { private OnClickServiceListener mOnClickServiceListener; @Override public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { View root = inflater.inflate(R.layout.fragment_service_list_viewer, container, false); mListAdapter = new ServiceListAdapter(getContext(), getServiceContainers(), false); mListView = (ListView) root.findViewById(R.id.device_connect_service_list_view); mListView.setAdapter(mListAdapter); mListView.setItemsCanFocus(true); mListView.setClickable(true); mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(final AdapterView<?> parent, final View view, final int position, final long id) { ServiceContainer service = (ServiceContainer) view.getTag(); if (service != null && mOnClickServiceListener != null) { mOnClickServiceListener.onClickService(service); } } }); mNoServiceView = root.findViewById(R.id.device_connect_service_list_message_no_service); Button newServiceButton = (Button) root.findViewById(R.id.device_connect_service_list_button_add_service); newServiceButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { ((DConnectServiceListActivity) getActivity()).showSettingActivity(); } }); mRemoveServiceButton = (Button) root.findViewById(R.id.device_connect_service_list_button_remove_service); mRemoveServiceButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { ((DConnectServiceListActivity) getActivity()).showServiceRemover(); } }); toggleView(); return root; } @Override public void onDestroy() { super.onDestroyView(); mOnClickServiceListener = null; } } public static class RemoverFragment extends AbstractViewerFragment implements OnCheckServiceListener { @Override public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { View root = inflater.inflate(R.layout.fragment_service_list_remover, container, false); mListAdapter = new ServiceListAdapter(getContext(), getServiceContainers(), true); mListAdapter.mOnCheckServiceListener = this; mListView = (ListView) root.findViewById(R.id.device_connect_service_list_view); mListView.setAdapter(mListAdapter); Button cancelButton = (Button) root.findViewById(R.id.device_connect_service_list_button_cancel); cancelButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { ((DConnectServiceListActivity) getActivity()).showServiceViewer(); } }); mNoServiceView = root.findViewById(R.id.device_connect_service_list_message_no_service); mRemoveServiceButton = (Button) root.findViewById(R.id.device_connect_service_list_button_remove_service); mRemoveServiceButton.setClickable(false); mRemoveServiceButton.setEnabled(false); mRemoveServiceButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { showRemovalConfirmation(); } }); return root; } private void showRemovalConfirmation() { new AlertDialog.Builder(getContext()) .setTitle(R.string.dialog_title_service_removal_confirmation) .setMessage(R.string.dialog_message_service_removal_confirmation) .setCancelable(false) .setPositiveButton(R.string.dialog_button_service_removal_ok, new DialogInterface.OnClickListener() { @Override public void onClick(final DialogInterface dialog, final int which) { for (ServiceContainer container : mListAdapter.getCheckedServiceList()) { mProvider.removeService(container.getId()); } } }) .setNegativeButton(R.string.dialog_button_service_removal_cancel, new DialogInterface.OnClickListener() { @Override public void onClick(final DialogInterface dialog, final int which) { // NOP. } }) .create().show(); } @Override public void onCheckStateChange(final ServiceContainer checkedService, final boolean isChecked) { boolean hasCheckedService = mListAdapter.getCheckedServiceList().size() > 0; mRemoveServiceButton.setEnabled(hasCheckedService); } } private static class ServiceListAdapter extends ArrayAdapter<ServiceContainer> { private final LayoutInflater mInflater; private final boolean mHasCheckbox; private final List<ServiceContainer> mCheckedServiceList = new ArrayList<ServiceContainer>(); private OnCheckServiceListener mOnCheckServiceListener; public ServiceListAdapter(final Context context, final List<ServiceContainer> objects, final boolean hasCheckbox) { super(context, 0, objects); mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mHasCheckbox = hasCheckbox; } @Override public View getView(final int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mInflater.inflate(R.layout.item_device_connect_service, null); } final ServiceContainer service = getItem(position); convertView.setTag(service); convertView.setBackgroundResource(service.isOnline() ? R.color.service_list_item_background_online : R.color.service_list_item_background_offline); TextView statusView = (TextView) convertView.findViewById(R.id.service_online_status); statusView.setText(service.isOnline() ? R.string.device_connect_service_is_online : R.string.device_connect_service_is_offline); TextView nameView = (TextView) convertView.findViewById(R.id.service_name); nameView.setText(service.getName()); CheckBox checkBox = (CheckBox) convertView.findViewById(R.id.device_connect_service_removal_checkbox); checkBox.setVisibility(mHasCheckbox && !service.isOnline() ? View.VISIBLE : View.GONE); if (!service.isOnline()) { checkBox.setEnabled(true); checkBox.setClickable(true); checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) { setChecked(service, isChecked); } }); } else { checkBox.setEnabled(false); checkBox.setClickable(false); checkBox.setChecked(false); } return convertView; } boolean isChecked(final ServiceContainer service) { for (ServiceContainer s : mCheckedServiceList) { if (s.getId().equals(service.getId())) { return true; } } return false; } void setChecked(final ServiceContainer service, final boolean isChecked) { if (isChecked) { if (!isChecked(service)) { mCheckedServiceList.add(service); } } else { for (Iterator<ServiceContainer> it = mCheckedServiceList.iterator(); ; it.hasNext()) { if (it.next().getId().equals(service.getId())) { it.remove(); break; } } } if (mOnCheckServiceListener != null) { mOnCheckServiceListener.onCheckStateChange(service, isChecked); } } ServiceContainer getServiceContainer(final String serviceId) { for (int i = 0; i < getCount(); i++) { ServiceContainer container = getItem(i); if (container.getId().equals(serviceId)) { return container; } } return null; } List<ServiceContainer> getCheckedServiceList() { return new ArrayList<ServiceContainer>(mCheckedServiceList); } void onStatusChange(final DConnectService service) { ServiceContainer container = getServiceContainer(service.getId()); container.setName(service.getName()); container.setOnline(service.isOnline()); notifyDataSetChanged(); } } private interface OnClickServiceListener extends Serializable { void onClickService(ServiceContainer service); } private interface OnCheckServiceListener { void onCheckStateChange(ServiceContainer checkedService, boolean isChecked); } public interface ListItemFilter { boolean doFilter(DConnectService service); } }