org.comixwall.pffw.InfoHosts.java Source code

Java tutorial

Introduction

Here is the source code for org.comixwall.pffw.InfoHosts.java

Source

/*
 * Copyright (C) 2017-2018 Soner Tari
 *
 * This file is part of PFFW.
 *
 * PFFW is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * PFFW is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with PFFW.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.comixwall.pffw;

import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import org.json.JSONArray;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;

import static org.comixwall.pffw.MainActivity.cache;
import static org.comixwall.pffw.MainActivity.controller;
import static org.comixwall.pffw.MainActivity.fragment;
import static org.comixwall.pffw.MainActivity.logger;
import static org.comixwall.pffw.Utils.processException;
import static org.comixwall.pffw.Utils.showMessage;

public class InfoHosts extends Fragment
        implements SwipeRefreshLayout.OnRefreshListener, RefreshTimer.OnTimeoutListener,
        RecyclerTouchListener.OnItemClickListener, ControllerTask.ControllerTaskListener {

    private InfoHostsCache mModuleCache;

    private RefreshTimer mTimer;
    private int mRefreshTimeout = 10;

    private SwipeRefreshLayout swipeRefresh;

    private String mDhcpdStatus;
    private String mNamedStatus;

    private TextView tvDhcpdStatus;
    private TextView tvNamedStatus;

    private ImageView ivDhcpdStatus;
    private ImageView ivNamedStatus;

    private JSONArray mDhcpdJsonArray;
    private final List<Process> mDhcpdList = new ArrayList<>();
    private ProcessRecyclerAdapter mDhcpdAdapter;

    private JSONArray mNamedJsonArray;
    private final List<Process> mNamedList = new ArrayList<>();
    private ProcessRecyclerAdapter mNamedAdapter;

    private JSONArray mArpTableJsonArray;
    private final List<Arp> mArpTableList = new ArrayList<>();
    private ArpTableRecyclerAdapter mArpTableAdapter;

    private JSONArray mLeasesJsonArray;
    private final List<Lease> mLeasesList = new ArrayList<>();
    private LeaseRecyclerAdapter mLeasesAdapter;

    private final RecyclerTouchListener.OnItemClickListener mArpTableItemClickListener = new RecyclerTouchListener.OnItemClickListener() {
        @Override
        public void onItemClick(View view) {
            TextView tvIp = view.findViewById(R.id.ip);
            TextView tvMac = view.findViewById(R.id.mac);

            int lines = 10;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                if (tvIp.getMaxLines() != 1) {
                    lines = 1;
                }
            }

            tvIp.setMaxLines(lines);
            tvMac.setMaxLines(lines);
        }
    };

    private final RecyclerTouchListener.OnItemClickListener mLeasesItemClickListener = new RecyclerTouchListener.OnItemClickListener() {
        @Override
        public void onItemClick(View view) {
            TextView tvStartEnd = view.findViewById(R.id.startEnd);
            TextView tvIp = view.findViewById(R.id.ip);
            TextView tvHost = view.findViewById(R.id.host);

            int lines = 10;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                if (tvIp.getMaxLines() != 1) {
                    lines = 1;
                }
            }

            tvStartEnd.setMaxLines(lines);
            tvIp.setMaxLines(lines);
            tvHost.setMaxLines(lines);
        }
    };

    private String mLastError;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        View view = inflater.inflate(R.layout.info_hosts, container, false);

        swipeRefresh = view.findViewById(R.id.swipeRefresh);
        swipeRefresh.setOnRefreshListener(this);

        tvDhcpdStatus = view.findViewById(R.id.dhcpdStatus);
        tvNamedStatus = view.findViewById(R.id.namedStatus);

        ivDhcpdStatus = view.findViewById(R.id.imageViewDhcpdStatus);
        ivNamedStatus = view.findViewById(R.id.imageViewNamedStatus);

        RecyclerView rvDhcpd = view.findViewById(R.id.recyclerViewDhcpd);
        // ATTENTION: Should use separate LayoutManager for each RecyclerView.
        rvDhcpd.setLayoutManager(new LinearLayoutManager(getActivity()));
        rvDhcpd.setItemAnimator(new DefaultItemAnimator());
        rvDhcpd.addItemDecoration(new RecyclerDivider(getActivity(), LinearLayoutManager.VERTICAL));
        mDhcpdAdapter = new ProcessRecyclerAdapter(mDhcpdList);
        rvDhcpd.setAdapter(mDhcpdAdapter);
        rvDhcpd.addOnItemTouchListener(new RecyclerTouchListener(getActivity(), this));

        RecyclerView rvNamed = view.findViewById(R.id.recyclerViewNamed);
        rvNamed.setLayoutManager(new LinearLayoutManager(getActivity()));
        rvNamed.setItemAnimator(new DefaultItemAnimator());
        rvNamed.addItemDecoration(new RecyclerDivider(getActivity(), LinearLayoutManager.VERTICAL));
        mNamedAdapter = new ProcessRecyclerAdapter(mNamedList);
        rvNamed.setAdapter(mNamedAdapter);
        rvNamed.addOnItemTouchListener(new RecyclerTouchListener(getActivity(), this));

        RecyclerView rvArpTable = view.findViewById(R.id.recyclerViewArpTable);
        rvArpTable.setLayoutManager(new LinearLayoutManager(getActivity()));
        rvArpTable.setItemAnimator(new DefaultItemAnimator());
        rvArpTable.addItemDecoration(new RecyclerDivider(getActivity(), LinearLayoutManager.VERTICAL));
        mArpTableAdapter = new ArpTableRecyclerAdapter(mArpTableList);
        rvArpTable.setAdapter(mArpTableAdapter);
        rvArpTable.addOnItemTouchListener(new RecyclerTouchListener(getActivity(), mArpTableItemClickListener));

        RecyclerView rvLeases = view.findViewById(R.id.recyclerViewLeases);
        rvLeases.setLayoutManager(new LinearLayoutManager(getActivity()));
        rvLeases.setItemAnimator(new DefaultItemAnimator());
        rvLeases.addItemDecoration(new RecyclerDivider(getActivity(), LinearLayoutManager.VERTICAL));
        mLeasesAdapter = new LeaseRecyclerAdapter(mLeasesList);
        rvLeases.setAdapter(mLeasesAdapter);
        rvLeases.addOnItemTouchListener(new RecyclerTouchListener(getActivity(), mLeasesItemClickListener));

        if (cache.infoHosts == null) {
            cache.infoHosts = new InfoHostsCache();
        }
        mModuleCache = cache.infoHosts;

        return view;
    }

    @Override
    public void onPause() {
        super.onPause();

        mModuleCache.bundle.putString("mDhcpdStatus", mDhcpdStatus);
        mModuleCache.bundle.putString("mNamedStatus", mNamedStatus);

        mModuleCache.mDhcpdJsonArray = mDhcpdJsonArray;
        mModuleCache.mNamedJsonArray = mNamedJsonArray;
        mModuleCache.mArpTableJsonArray = mArpTableJsonArray;
        mModuleCache.mLeasesJsonArray = mLeasesJsonArray;

        // ATTENTION: It is very important to cancel the timer
        mTimer.cancel();
    }

    @Override
    public void onResume() {
        super.onResume();

        fragment = this;
        mDhcpdStatus = mModuleCache.bundle.getString("mDhcpdStatus");

        if (mDhcpdStatus == null) {
            getInfo();
        } else {

            mNamedStatus = mModuleCache.bundle.getString("mNamedStatus");

            mDhcpdJsonArray = mModuleCache.mDhcpdJsonArray;
            mNamedJsonArray = mModuleCache.mNamedJsonArray;
            mArpTableJsonArray = mModuleCache.mArpTableJsonArray;
            mLeasesJsonArray = mModuleCache.mLeasesJsonArray;

            updateInfo();
        }

        // ATTENTION: Schedule the timer here, not in onCreateView(), because mRefreshTimeout may be updated in getInfo()
        mTimer = new RefreshTimer((MainActivity) getActivity(), this);
        mTimer.start(mRefreshTimeout);
    }

    @Override
    public void onTimeout() {
        getInfo();
    }

    @Override
    public void executePreTask() {
    }

    @Override
    public void preExecute() {
        swipeRefresh.setRefreshing(true);
    }

    /**
     * Fetch dhcpd and named status, arp table and dhcpd leases.
     *
     * @return True on success, false on failure.
     */
    @Override
    public boolean executeTask() {
        Boolean retval = true;
        try {
            String output = controller.execute("dhcpd", "IsRunning");
            mDhcpdStatus = new JSONArray(output).get(2).toString();

            output = controller.execute("dhcpd", "GetProcList");
            JSONArray jsonArray = new JSONArray(output);
            mDhcpdJsonArray = new JSONArray(jsonArray.get(0).toString());

            output = controller.execute("named", "IsRunning");
            mNamedStatus = new JSONArray(output).get(2).toString();

            output = controller.execute("named", "GetProcList");
            jsonArray = new JSONArray(output);
            mNamedJsonArray = new JSONArray(jsonArray.get(0).toString());

            output = controller.execute("dhcpd", "GetArpTable");
            jsonArray = new JSONArray(output);
            mArpTableJsonArray = new JSONArray(jsonArray.get(0).toString());

            output = controller.execute("dhcpd", "GetLeases");
            jsonArray = new JSONArray(output);
            mLeasesJsonArray = new JSONArray(jsonArray.get(0).toString());

            output = controller.execute("pf", "GetReloadRate");
            int timeout = Integer.parseInt(new JSONArray(output).get(0).toString());
            mRefreshTimeout = timeout < 10 ? 10 : timeout;

        } catch (Exception e) {
            mLastError = processException(e);
            retval = false;
        }
        return retval;
    }

    @Override
    public void postExecute(boolean result) {
        if (result) {
            updateInfo();
        } else {
            showMessage(this, "Error: " + mLastError);
        }

        swipeRefresh.setRefreshing(false);
    }

    @Override
    public void executeOnCancelled() {
        swipeRefresh.setRefreshing(false);
    }

    private void getInfo() {
        ControllerTask.run(this, this);
    }

    private void updateInfo() {

        Utils.updateStatusViews(mDhcpdStatus, ivDhcpdStatus, tvDhcpdStatus, getString(R.string.dhcp_server));

        updateProcList(mDhcpdJsonArray, mDhcpdList);
        mDhcpdAdapter.notifyDataSetChanged();

        Utils.updateStatusViews(mNamedStatus, ivNamedStatus, tvNamedStatus, getString(R.string.dns_server));

        updateProcList(mNamedJsonArray, mNamedList);
        mNamedAdapter.notifyDataSetChanged();

        updateArpList(mArpTableJsonArray, mArpTableList);
        mArpTableAdapter.notifyDataSetChanged();

        updateLeaseList(mLeasesJsonArray, mLeasesList);
        mLeasesAdapter.notifyDataSetChanged();
    }

    private void updateProcList(JSONArray jsonArray, List<Process> list) {
        try {
            list.clear();

            int i = 0;
            while (i < jsonArray.length()) {
                list.add(Process.newInstance(jsonArray.getJSONArray(i++)));
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.warning("updateProcList exception: " + e.toString());
        }
    }

    private void updateArpList(JSONArray jsonArray, List<Arp> list) {
        try {
            list.clear();

            int i = 0;
            while (i < jsonArray.length()) {
                list.add(Arp.newInstance(jsonArray.getJSONObject(i), i + 1));
                i++;
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.warning("updateArpList exception: " + e.toString());
        }
    }

    private void updateLeaseList(JSONArray jsonArray, List<Lease> list) {
        try {
            list.clear();

            int i = 0;
            while (i < jsonArray.length()) {
                list.add(Lease.newInstance(jsonArray.getJSONObject(i), i + 1));
                i++;
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.warning("updateLeaseList exception: " + e.toString());
        }
    }

    @Override
    public void onRefresh() {
        getInfo();
    }

    /**
     * Change the size of certain text views on touch if the content does not fit.
     *
     * @param view Container view
     */
    @Override
    public void onItemClick(View view) {
        TextView tvCommand = view.findViewById(R.id.command);
        TextView tvUserGroup = view.findViewById(R.id.others);

        int lines = 10;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            if (tvCommand.getMaxLines() != 1) {
                lines = 1;
            }
        }

        tvCommand.setMaxLines(lines);
        tvUserGroup.setMaxLines(lines);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        if (id == R.id.menuRefresh) {
            getInfo();
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

/**
 * Arp table entry.
 */
class Arp {
    String num;
    String ip;
    String mac;
    String _if;
    String expire;

    static Arp newInstance(JSONObject arpEntry, int line) {
        Arp arp = new Arp();
        try {
            arp.num = Integer.toString(line);
            arp.ip = arpEntry.getString("IP");
            arp.mac = arpEntry.getString("MAC");
            arp._if = arpEntry.getString("Interface");
            arp.expire = arpEntry.getString("Expire");
        } catch (Exception e) {
            e.printStackTrace();
            logger.warning("Arp newInstance exception: " + e.toString());
        }
        return arp;
    }
}

class ArpTableRecyclerAdapter extends RecyclerView.Adapter<ArpTableRecyclerAdapter.ArpViewHolder> {

    private final List<Arp> arpList;

    class ArpViewHolder extends RecyclerView.ViewHolder {
        final TextView onIf;
        final TextView ip;
        final TextView expire;
        final TextView number;
        final TextView mac;
        final TextView image;

        ArpViewHolder(View view) {
            super(view);
            onIf = view.findViewById(R.id.onIf);
            ip = view.findViewById(R.id.ip);
            expire = view.findViewById(R.id.expire);
            number = view.findViewById(R.id.number);
            mac = view.findViewById(R.id.mac);
            image = view.findViewById(R.id.image);
        }
    }

    ArpTableRecyclerAdapter(List<Arp> list) {
        this.arpList = list;
    }

    @Override
    public ArpViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.arp, parent, false);

        return new ArpViewHolder(itemView);
    }

    /**
     * Populate recycler view item.
     *
     * @param holder   Container view.
     * @param position Item position.
     */
    @Override
    public void onBindViewHolder(ArpViewHolder holder, int position) {

        Arp arp = arpList.get(position);

        holder.onIf.setText(String.format(holder.onIf.getResources().getString(R.string.on_if), arp._if));
        holder.ip.setText(arp.ip);
        holder.number.setText(arp.num);
        holder.mac.setText(arp.mac);
        holder.expire.setText(arp.expire);

        int image;
        String caption;

        switch (arp.expire) {
        case "expired":
            image = R.drawable.block;
            caption = "E";
            break;
        case "permanent":
            image = R.drawable.pass;
            caption = "P";
            break;
        default:
            image = R.drawable.match;
            caption = "A";
            break;
        }

        holder.image.setBackgroundResource(image);
        holder.image.setText(caption);
    }

    @Override
    public int getItemCount() {
        return arpList.size();
    }
}

/**
 * Dhcp lease entry.
 */
class Lease {
    String num;
    String ip;
    public String start;
    public String end;
    String mac;
    String host;
    String status;

    static Lease newInstance(JSONObject leaseEntry, int line) {
        Lease lease = new Lease();
        try {
            lease.num = Integer.toString(line);
            lease.ip = leaseEntry.getString("IP");
            lease.start = leaseEntry.getString("Start");
            lease.end = leaseEntry.getString("End");
            lease.mac = leaseEntry.getString("MAC");
            lease.host = leaseEntry.getString("Host");
            lease.status = leaseEntry.getString("Status");
        } catch (Exception e) {
            e.printStackTrace();
            logger.warning("Lease newInstance exception: " + e.toString());
        }
        return lease;
    }
}

class LeaseRecyclerAdapter extends RecyclerView.Adapter<LeaseRecyclerAdapter.LeaseViewHolder> {

    private final List<Lease> leaseList;

    class LeaseViewHolder extends RecyclerView.ViewHolder {
        final TextView startEnd;
        final TextView ip;
        final TextView status;
        final TextView number;
        final TextView host;

        LeaseViewHolder(View view) {
            super(view);
            startEnd = view.findViewById(R.id.startEnd);
            ip = view.findViewById(R.id.ip);
            status = view.findViewById(R.id.status);
            number = view.findViewById(R.id.number);
            host = view.findViewById(R.id.host);
        }
    }

    LeaseRecyclerAdapter(List<Lease> list) {
        this.leaseList = list;
    }

    @Override
    public LeaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.lease, parent, false);

        return new LeaseViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(LeaseViewHolder holder, int position) {

        Lease lease = leaseList.get(position);

        holder.startEnd.setText(lease.start + " - " + lease.end);
        holder.ip.setText(lease.ip + " - " + lease.mac);
        holder.number.setText(lease.num);
        holder.host.setText(lease.host);
        // TODO: Check why Json assigns "null" to empty string?
        holder.status.setText(lease.status.equals("null") ? "" : lease.status);

    }

    @Override
    public int getItemCount() {
        return leaseList.size();
    }
}

class InfoHostsCache {
    public final Bundle bundle = new Bundle();

    JSONArray mDhcpdJsonArray;
    JSONArray mNamedJsonArray;
    JSONArray mArpTableJsonArray;
    JSONArray mLeasesJsonArray;
}