com.sakisds.icymonitor.activities.ConnectionActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.sakisds.icymonitor.activities.ConnectionActivity.java

Source

/*
 * Copyright 2013 Thanasis Georgiou
 *
 *    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.sakisds.icymonitor.activities;

import android.app.AlertDialog;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.*;
import android.widget.*;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.JsonHttpResponseHandler;
import com.loopj.android.http.RequestParams;
import com.sakisds.icymonitor.R;
import com.sakisds.icymonitor.dataobj.ComputerInfo;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.sql.Timestamp;
import java.util.LinkedList;
import java.util.List;

/**
 * First activity presented to user.
 * Show a list of servers and let the user pick which one to connect to.
 * Created by sakisds on 23/05/13.
 */
public class ConnectionActivity extends ListActivity {
    public final static String ACCEPTED_SERVER_VERSION = "2.0.1";
    public final static String UPDATE_SERVER_VERSION = "2.0";

    private SharedPreferences mSettings;
    private ComputerInfo[] mData;

    private AsyncHttpClient mClient = new AsyncHttpClient();

    // GCM stuff
    public static final String PROPERTY_REG_ID = "registration_id";
    private static final String PROPERTY_APP_VERSION = "appVersion";
    private static final String PROPERTY_ON_SERVER_EXPIRATION_TIME = "onServerExpirationTimeMs";
    public static final long REGISTRATION_EXPIRY_TIME_MS = 1000 * 3600 * 24 * 14; // 14 days
    String SENDER_ID = "703448116465";

    GoogleCloudMessaging gcm;
    Context context;
    String regid;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_connection);
        mSettings = getSharedPreferences(MainViewActivity.SHAREDPREFS_FILE, 0);

        refreshListAdapter();

        ListView listView = getListView();

        listView.setLongClickable(true);
        listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
        listView.setMultiChoiceModeListener(new MultipleChoiceListener());

        getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
            public void onItemClick(AdapterView parent, View v, int position, long id) {
                String url = "http://" + ((TextView) v.findViewById(R.id.text_address)).getText().toString();
                connect(url);
            }
        });

        // Http Client
        mClient.setMaxRetriesAndTimeout(2, 2000);

        // Setup GCM
        context = getApplicationContext();
        regid = getRegistrationId(context);

        if (regid.length() == 0) {
            registerBackground();
        }
        gcm = GoogleCloudMessaging.getInstance(this);
    }

    /**
     * Gets the current registration id for application on GCM service.
     * <p/>
     * If result is empty, the registration has failed.
     *
     * @return registration id, or empty string if the registration is not
     * complete.
     */
    private String getRegistrationId(Context context) {
        final SharedPreferences prefs = getGCMPreferences(context);
        String registrationId = prefs.getString(PROPERTY_REG_ID, "");
        if (registrationId.length() == 0) {
            Log.v("gcm", "Registration not found.");
            return "";
        }
        // check if app was updated; if so, it must clear registration id to
        // avoid a race condition if GCM sends a message
        int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE);
        int currentVersion = getAppVersion(context);
        if (registeredVersion != currentVersion || isRegistrationExpired()) {
            Log.v("gcm", "App version changed or registration expired.");
            return "";
        }
        return registrationId;
    }

    /**
     * @return Application's {@code SharedPreferences}.
     */
    private SharedPreferences getGCMPreferences(Context context) {
        return getSharedPreferences(ConnectionActivity.class.getSimpleName(), Context.MODE_PRIVATE);
    }

    /**
     * @return Application's version code from the {@code PackageManager}.
     */
    private static int getAppVersion(Context context) {
        try {
            PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
            return packageInfo.versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            // should never happen
            throw new RuntimeException("Could not get package name: " + e);
        }
    }

    /**
     * Checks if the registration has expired.
     * <p/>
     * <p>To avoid the scenario where the device sends the registration to the
     * server but the server loses it, the app developer may choose to re-register
     * after REGISTRATION_EXPIRY_TIME_MS.
     *
     * @return true if the registration has expired.
     */
    private boolean isRegistrationExpired() {
        final SharedPreferences prefs = getGCMPreferences(context);
        // checks if the information is not stale
        long expirationTime = prefs.getLong(PROPERTY_ON_SERVER_EXPIRATION_TIME, -1);
        return System.currentTimeMillis() > expirationTime;
    }

    /**
     * Registers the application with GCM servers asynchronously.
     * <p/>
     * Stores the registration id, app versionCode, and expiration time in the
     * application's shared preferences.
     */
    private void registerBackground() {
        new AsyncTask<String, Void, Void>() {
            @Override
            protected Void doInBackground(String... params) {
                String msg = "";
                try {
                    if (gcm == null) {
                        gcm = GoogleCloudMessaging.getInstance(context);
                    }
                    regid = gcm.register(SENDER_ID);
                    msg = "Device registered, registration id=" + regid;
                    setRegistrationId(context, regid);
                } catch (IOException ex) {
                }
                return null;
            }
        }.execute(null, null, null);
    }

    /**
     * Stores the registration id, app versionCode, and expiration time in the
     * application's {@code SharedPreferences}.
     *
     * @param context application's context.
     * @param regId   registration id
     */
    private void setRegistrationId(Context context, String regId) {
        final SharedPreferences prefs = getGCMPreferences(context);
        int appVersion = getAppVersion(context);
        Log.v("gcm", "Saving regId on app version " + appVersion);
        SharedPreferences.Editor editor = prefs.edit();
        editor.putString(PROPERTY_REG_ID, regId);
        editor.putInt(PROPERTY_APP_VERSION, appVersion);
        long expirationTime = System.currentTimeMillis() + REGISTRATION_EXPIRY_TIME_MS;

        Log.v("gcm", "Setting registration expiry time to " + new Timestamp(expirationTime));
        editor.putLong(PROPERTY_ON_SERVER_EXPIRATION_TIME, expirationTime);
        editor.commit();
    }

    /**
     * Connect to URL
     *
     * @param url URL to connect.
     */
    private void connect(final String url) {
        // Create client
        RequestParams params = new RequestParams("id", mSettings.getLong("device_id", -1));

        // Create a dialog
        final ProgressDialog progress = ProgressDialog.show(this, "", getResources().getString(R.string.connecting),
                false);

        final Context context = this;
        // Send request
        mClient.get(url + "/auth", params, new JsonHttpResponseHandler() {
            @Override
            public void onSuccess(JSONObject response) {
                progress.dismiss();
                try {
                    String version = response.getString("Version");
                    if (version.equals(ACCEPTED_SERVER_VERSION)) {
                        String auth = response.getString("Auth");
                        if (auth.equals("OK")) {
                            Intent intent = new Intent(getBaseContext(), MainViewActivity.class);
                            intent.putExtra(MainViewActivity.EXTRA_ADDRESS, url);
                            startActivity(intent);
                        } else {
                            showErrorDialog(R.string.error_could_not_connect_title, R.string.error_authentication);
                        }
                    } else if (version.equals(UPDATE_SERVER_VERSION)) {
                        showErrorDialog(R.string.error_could_not_connect_title,
                                R.string.error_outdated_server_update);
                    } else {
                        showErrorDialog(R.string.error_outdated_server, R.string.error_outdated_server_long);
                    }
                } catch (JSONException e) {
                    showErrorDialog(R.string.error_could_not_connect_title, R.string.error_invalid_response);
                }
            }

            @Override
            public void onFailure(Throwable e, JSONObject response) {
                progress.dismiss();
                showErrorDialog(R.string.error_could_not_connect_title, R.string.error_could_not_connect);
            }
        });
    }

    /**
     * Refresh the list.
     */
    private void refreshListAdapter() {
        int serverCount = mSettings.getInt("serverCount", 0);
        // Populate data
        mData = new ComputerInfo[serverCount];
        for (int i = 0; i < serverCount; i++) {
            mData[i] = new ComputerInfo(mSettings.getString("server_" + String.valueOf(i) + "_name", "Null"),
                    mSettings.getString("server_" + String.valueOf(i) + "_address", "Null"),
                    mSettings.getInt("server_" + String.valueOf(i) + "_type", 0));
        }
        // Set adapter
        setListAdapter(new ComputersListArrayAdapter(this, mData));

    }

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

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu_connection_list, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.item_add:
            startActivity(new Intent(this, DetectServerActivity.class));
            return true;
        case R.id.item_settings:
            startActivity(new Intent(this, SettingsActivity.class));
            return true;
        case R.id.item_about:
            startActivity(new Intent(this, AboutActivity.class));
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    void showErrorDialog(int title, int message) {
        AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this);
        dlgAlert.setTitle(getResources().getString(title));
        dlgAlert.setMessage(getResources().getString(message));
        dlgAlert.setPositiveButton("OK", null);
        dlgAlert.setCancelable(true);
        dlgAlert.create().show();
    }

    private void deleteSelectedItems(final SparseBooleanArray checked) {
        String message = getResources().getQuantityString(R.plurals.deleteComputers, checked.size());

        new AlertDialog.Builder(this).setMessage(message).setCancelable(false)
                .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        ListView listView = getListView();

                        int cntChoice = listView.getCount();

                        int removedCount = 0;

                        for (int i = 0; i < cntChoice; i++) {
                            if (checked.get(i)) {
                                mData[i].setToBeRemoved();
                                removedCount++;
                            }
                        }

                        List resultData = new LinkedList();
                        for (ComputerInfo comp : mData) {
                            if (!comp.isToBeRemoved()) {
                                //noinspection unchecked
                                resultData.add(comp);
                            }
                        }

                        int newLen = mData.length - removedCount;

                        SharedPreferences.Editor editor = mSettings.edit();
                        editor.putInt("serverCount", newLen);

                        ComputerInfo[] names = (ComputerInfo[]) resultData.toArray(mData);

                        // Save to settings
                        for (int i = 0; i < newLen; i++) {
                            editor.putString("server_" + String.valueOf(i) + "_name", names[i].getName());
                            editor.putString("server_" + String.valueOf(i) + "_address", names[i].getAddress());
                            editor.putInt("server_" + String.valueOf(i) + "_type", names[i].getType());
                        }
                        editor.commit();

                        refreshListAdapter();
                    }
                }).setNegativeButton(R.string.no, null).show();
    }

    private class ComputersListArrayAdapter extends ArrayAdapter<ComputerInfo> {

        private final Context mContext;

        public ComputersListArrayAdapter(Context context, ComputerInfo[] data) {
            super(context, R.layout.list_item_computer, data);
            mContext = context;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            View rowView = inflater.inflate(R.layout.list_item_computer, parent, false);

            ((TextView) rowView.findViewById(R.id.text_name)).setText(mData[position].getName());
            ((TextView) rowView.findViewById(R.id.text_address)).setText(mData[position].getAddress().substring(7));
            ImageView imageViewType = (ImageView) rowView.findViewById(R.id.icon);
            switch (mData[position].getType()) {
            case 0:
                imageViewType.setImageResource(R.drawable.ic_type_desktop);
                break;
            case 1:
                imageViewType.setImageResource(R.drawable.ic_type_laptop);
                break;
            case 2:
                imageViewType.setImageResource(R.drawable.ic_type_server);
                break;
            }

            return rowView;
        }
    }

    private class MultipleChoiceListener implements ListView.MultiChoiceModeListener {
        @Override
        public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
            final int checkedCount = getListView().getCheckedItemCount();

            final String title = getResources().getQuantityString(R.plurals.computerSelected, checkedCount,
                    checkedCount);
            mode.setTitle(title);
        }

        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            // Respond to clicks on the actions in the CAB
            switch (item.getItemId()) {
            case R.id.item_delete:
                SparseBooleanArray checked = getListView().getCheckedItemPositions().clone();
                deleteSelectedItems(checked);
                mode.finish(); // Action picked, so close the CAB
                return true;
            default:
                return false;
            }
        }

        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            // Inflate the menu for the CAB
            MenuInflater inflater = mode.getMenuInflater();
            inflater.inflate(R.menu.context_computers, menu);
            return true;
        }

        @Override
        public void onDestroyActionMode(ActionMode mode) {
        }

        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            return true;
        }
    }
}