com.google.android.apps.tvremote.PairingActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.google.android.apps.tvremote.PairingActivity.java

Source

/*
 * Copyright (C) 2010 Google Inc.  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 com.google.android.apps.tvremote;

import java.io.IOException;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;

import com.cnm.cnmrc.R;
import com.cnm.cnmrc.popup.PopupGtvPairingDialog;
import com.cnm.cnmrc.popup.PopupGtvWaitingPairingCode;
import com.google.polo.exception.PoloException;
import com.google.polo.pairing.ClientPairingSession;
import com.google.polo.pairing.PairingContext;
import com.google.polo.pairing.PairingListener;
import com.google.polo.pairing.PairingSession;
import com.google.polo.pairing.message.EncodingOption;
import com.google.polo.ssl.DummySSLSocketFactory;
import com.google.polo.wire.PoloWireInterface;
import com.google.polo.wire.WireFormat;

/**
 * An Activity that handles pairing process.
 * 
 * Pairing activity establishes and handles Polo pairing session. If needed, it
 * displays dialog to enter secret code.
 * 
 */
public class PairingActivity extends CoreServiceActivity {

    private static final String LOG_TAG = "PairingActivity";
    private static final String EXTRA_REMOTE_DEVICE = "remote_device";
    private static final String EXTRA_PAIRING_RESULT = "pairing_result";
    private static final String REMOTE_NAME = Build.MANUFACTURER + " " + Build.MODEL;

    /**
     * Result for pairing failure due to connection problem.
     */
    public static final int RESULT_CONNECTION_FAILED = RESULT_FIRST_USER;

    /**
     * Result for pairing failure due to invalid code or protocol error.
     */
    public static final int RESULT_PAIRING_FAILED = RESULT_FIRST_USER + 1;

    /**
     * Enumeration that encapsulates all valid pairing results.
     */
    private enum Result {
        /**
         * Pairing successful.
         */
        SUCCEEDED(Activity.RESULT_OK),
        /**
         * Pairing failed - connection problem.
         */
        FAILED_CONNECTION(PairingActivity.RESULT_CONNECTION_FAILED),
        /**
         * Pairing failed - canceled.
         */
        FAILED_CANCELED(Activity.RESULT_CANCELED),
        /**
         * Pairing failed - invalid secret.
         */
        FAILED_SECRET(PairingActivity.RESULT_PAIRING_FAILED);

        private final int resultCode;

        Result(int resultCode) {
            this.resultCode = resultCode;
        }
    }

    private Handler handler;

    /**
     * Pairing dialog.
     */
    private AlertDialog alertDialog;

    private PairingClientThread pairing;

    private ProgressDialog progressDialog;
    private RemoteDevice remoteDevice;

    private boolean isPopupShowing = false;
    private final String TAG_FRAGMENT_POPUP = "tvremote-pairing-popup";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        handler = new Handler();

        progressDialog = buildProgressDialog();
        //progressDialog.show();

        // hwang
        showPopupWaitingPairingCode();

        remoteDevice = getIntent().getParcelableExtra(EXTRA_REMOTE_DEVICE);
        if (remoteDevice == null) {
            throw new IllegalStateException();
        }
    }

    @Override
    protected void onPause() {
        if (pairing != null) {
            pairing.cancel();
            pairing = null;
        }
        hideKeyboard();
        super.onPause();
    }

    public static Intent createIntent(Context context, RemoteDevice remoteDevice) {
        Intent intent = new Intent(context, PairingActivity.class);
        intent.putExtra(EXTRA_REMOTE_DEVICE, remoteDevice);
        return intent;
    }

    // --------------------
    // waiting pairing code
    // --------------------
    private ProgressDialog buildProgressDialog() {
        ProgressDialog dialog = new ProgressDialog(this);
        dialog.setMessage(getString(R.string.pairing_waiting));
        dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
            public boolean onKey(DialogInterface dialogInterface, int which, KeyEvent event) {
                if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
                    cancelPairing();
                    return true;
                }
                return false;
            }
        });
        dialog.setButton(getString(R.string.pairing_cancel), new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialogInterface, int which) {
                cancelPairing();
            }
        });
        return dialog;
    }

    private void showPopupWaitingPairingCode() {
        if (isPopupShowing) {
            Fragment f = getSupportFragmentManager().findFragmentByTag(TAG_FRAGMENT_POPUP);
            getSupportFragmentManager().beginTransaction().remove(f).commit();
            isPopupShowing = false;
        }

        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        PopupGtvWaitingPairingCode popupGtvWaitingPairingCode = new PopupGtvWaitingPairingCode();
        popupGtvWaitingPairingCode.show(ft, TAG_FRAGMENT_POPUP);

        isPopupShowing = true;
    }

    // --------------------
    // start pairing
    // --------------------
    @Override
    protected void onServiceAvailable(CoreService coreService) {
        startPairing();
    }

    @Override
    protected void onServiceDisconnecting(CoreService coreService) {
        cancelPairing();
    }

    private void startPairing() {
        if (pairing != null) {
            Log.v(LOG_TAG, "Already pairing - cancel first.");
            return;
        }
        Log.v(LOG_TAG, "Starting pairing with " + remoteDevice);
        pairing = new PairingClientThread();
        new Thread(pairing).start();
    }

    public void cancelPairing() {
        if (pairing != null) {
            pairing.cancel();
            pairing = null;
        }
        dismissProgressDialog();
        finishedPairing(Result.FAILED_CANCELED);
    }

    private void dismissProgressDialog() {
        if (progressDialog != null) {
            progressDialog.dismiss();
            progressDialog = null;
        }

        // hwang
        if (isPopupShowing) {
            Fragment f = getSupportFragmentManager().findFragmentByTag(TAG_FRAGMENT_POPUP);
            getSupportFragmentManager().beginTransaction().remove(f).commit();
            isPopupShowing = false;
        }
    }

    private void finishedPairing(Result result) {
        Intent resultIntent = new Intent();
        resultIntent.putExtra(EXTRA_PAIRING_RESULT, result);
        setResult(result.resultCode);
        hideKeyboard(); // hwang
        finish();
    }

    // ----------------------
    // create Pairing Dialog
    // ----------------------
    private AlertDialog createPairingDialog(final PairingClientThread client) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);

        // hwang 2013-12-01
        // R.layout.pairing?  ...
        View view = LayoutInflater.from(this).inflate(R.layout.pairing, null);
        final EditText pinEditText = (EditText) view.findViewById(R.id.pairing_pin_entry);

        builder.setPositiveButton(R.string.pairing_ok, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                alertDialog = null;
                client.setSecret(pinEditText.getText().toString());
            }
        }).setNegativeButton(R.string.pairing_cancel, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                alertDialog = null;
                client.cancel();
            }
        }).setCancelable(false).setTitle(R.string.pairing_label).setMessage(remoteDevice.getName()).setView(view);
        return builder.create();
    }

    private void showPopupPairingDialog(final PairingClientThread client) {
        //alertDialog = null;

        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        PopupGtvPairingDialog popupGtvPairingDialog = new PopupGtvPairingDialog(client);
        popupGtvPairingDialog.show(ft, PopupGtvPairingDialog.class.getSimpleName());

        //showKeyboard();
    }

    public void makeAlertDialogNull() {
        alertDialog = null;

        // hwang 2013-12-01
        hideKeyboard();
    }

    public String getRemoteDeviceName() {
        if (remoteDevice != null)
            return remoteDevice.getName();

        return "";
    }

    /**
     * Pairing client thread, that handles pairing logic.
     * 
     */
    public final class PairingClientThread extends Thread {
        private String secret;
        private boolean isCancelling;

        public synchronized void setSecret(String secretEntered) {
            if (secret != null) {
                throw new IllegalStateException("Secret already set: " + secret);
            }
            secret = secretEntered;
            notify();
        }

        public void cancel() {
            synchronized (this) {
                Log.d(LOG_TAG, "Cancelling: " + this);
                isCancelling = true;
                notify();
            }
            try {
                join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        private synchronized String getSecret() {
            if (isCancelling) {
                return null;
            }
            if (secret != null) {
                return secret;
            }
            try {
                wait();
            } catch (InterruptedException e) {
                Log.d(LOG_TAG, "Exception occurred", e);
                return null;
            }
            return secret;
        }

        @Override
        public void run() {
            Result result = Result.FAILED_CONNECTION;
            try {
                SSLSocketFactory socketFactory;
                try {
                    socketFactory = DummySSLSocketFactory.fromKeyManagers(getKeyStoreManager().getKeyManagers());
                } catch (GeneralSecurityException e) {
                    throw new IllegalStateException("Cannot build socket factory", e);
                }

                SSLSocket socket;
                try {
                    socket = (SSLSocket) socketFactory.createSocket(remoteDevice.getAddress(),
                            remoteDevice.getPort());
                } catch (UnknownHostException e) {
                    return;
                } catch (IOException e) {
                    return;
                }

                PairingContext context;
                try {
                    context = PairingContext.fromSslSocket(socket, false);
                } catch (PoloException e) {
                    return;
                } catch (IOException e) {
                    return;
                }

                PoloWireInterface protocol = WireFormat.PROTOCOL_BUFFERS.getWireInterface(context);
                ClientPairingSession pairingSession = new ClientPairingSession(protocol, context, "AnyMote",
                        REMOTE_NAME);

                EncodingOption hexEnc = new EncodingOption(EncodingOption.EncodingType.ENCODING_HEXADECIMAL, 4);
                pairingSession.addInputEncoding(hexEnc);
                pairingSession.addOutputEncoding(hexEnc);

                PairingListener listener = new PairingListener() {
                    public void onSessionEnded(PairingSession session) {
                        Log.d(LOG_TAG, "onSessionEnded: " + session);
                    }

                    public void onSessionCreated(PairingSession session) {
                        Log.d(LOG_TAG, "onSessionCreated: " + session);
                    }

                    public void onPerformOutputDeviceRole(PairingSession session, byte[] gamma) {
                        Log.d(LOG_TAG, "onPerformOutputDeviceRole: " + session + ", "
                                + session.getEncoder().encodeToString(gamma));
                    }

                    public void onPerformInputDeviceRole(PairingSession session) {
                        showPairingDialog(PairingClientThread.this);

                        Log.d(LOG_TAG, "onPerformInputDeviceRole: " + session);
                        String secret = getSecret();
                        Log.d(LOG_TAG, "Got: " + secret + " " + isCancelling);
                        if (!isCancelling && secret != null) {
                            try {
                                byte[] secretBytes = session.getEncoder().decodeToBytes(secret);
                                session.setSecret(secretBytes);
                            } catch (IllegalArgumentException exception) {
                                Log.d(LOG_TAG, "Exception while decoding secret: ", exception);
                                session.teardown();
                            } catch (IllegalStateException exception) {
                                // ISE may be thrown when session is currently terminating
                                Log.d(LOG_TAG, "Exception while setting secret: ", exception);
                                session.teardown();
                            }
                        } else {
                            session.teardown();
                        }
                    }

                    public void onLogMessage(LogLevel level, String message) {
                        Log.d(LOG_TAG, "Log: " + message + " (" + level + ")");
                    }
                };

                boolean ret = pairingSession.doPair(listener);
                if (ret) {
                    Log.d(LOG_TAG, "Success");
                    getKeyStoreManager().storeCertificate(context.getServerCertificate());
                    result = Result.SUCCEEDED;
                } else if (isCancelling) {
                    result = Result.FAILED_CANCELED;
                } else {
                    result = Result.FAILED_SECRET;
                }
            } finally {
                sendPairingResult(result);
            }
        }
    }

    private void showPairingDialog(final PairingClientThread client) {
        handler.post(new Runnable() {
            public void run() {
                dismissProgressDialog();
                if (pairing == null) {
                    return;
                }

                alertDialog = createPairingDialog(client);
                //alertDialog.show();

                // hwang
                showPopupPairingDialog(client);

                // Focus and show keyboard
                //View pinView = alertDialog.findViewById(R.id.pairing_pin_entry);
                //pinView.requestFocus();
                //showKeyboard();

            }
        });
    }

    private void sendPairingResult(final Result pairingResult) {
        handler.post(new Runnable() {
            public void run() {
                if (alertDialog != null) {
                    hideKeyboard();
                    alertDialog.dismiss();
                }
                finishedPairing(pairingResult);
            }
        });
    }

    // --------------------
    // keyboard
    // --------------------
    public void hideKeyboard() {
        InputMethodManager manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        manager.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, 0);
    }

    public void showKeyboard() {
        Log.e("hwang-tvremote", "hhhhhhh : onWindowFocusChanged()");
        InputMethodManager manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        manager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
    }
}