// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
package com.google.appinventor.components.runtime;
import java.util.Formatter;
import java.security.MessageDigest;
import android.app.Activity;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.ConnectivityManager;
import android.net.DhcpInfo;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.util.Log;
import com.google.appinventor.components.annotations.DesignerComponent;
import com.google.appinventor.components.annotations.SimpleEvent;
import com.google.appinventor.components.annotations.SimpleFunction;
import com.google.appinventor.components.annotations.SimpleObject;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.common.YaVersion;
import com.google.appinventor.components.runtime.util.AppInvHTTPD;
import com.google.appinventor.components.runtime.util.PackageInstaller;
import com.google.appinventor.components.runtime.Form;
import com.google.appinventor.components.runtime.ReplForm;
/**
* Component for obtaining Phone Information. Currently supports
* obtaining the IP Address of the phone and whether or not it is
* connected via a WiFi connection.
*
* @author lmercer@mit.edu (Logan Mercer)
*
*/
@DesignerComponent(version = YaVersion.PHONESTATUS_COMPONENT_VERSION,
description = "Component that returns information about the phone.",
category = ComponentCategory.INTERNAL,
nonVisible = true,
iconName = "images/phoneip.png")
@SimpleObject
public class PhoneStatus extends AndroidNonvisibleComponent implements Component {
private static Activity activity;
private static final String LOG_TAG = "PhoneStatus";
private final Form form;
private static PhoneStatus mainInstance = null;
public PhoneStatus(ComponentContainer container) {
super(container.$form());
this.form = container.$form();
activity = container.$context();
if (mainInstance == null) { // First one?
mainInstance = this;
}
}
@SimpleFunction(description = "Returns the IP address of the phone in the form of a String")
public static String GetWifiIpAddress() {
DhcpInfo ip;
Object wifiManager = activity.getSystemService("wifi");
ip = ((WifiManager) wifiManager).getDhcpInfo();
int s_ipAddress= ip.ipAddress;
String ipAddress;
if (isConnected())
ipAddress = intToIp(s_ipAddress);
else
ipAddress = "Error: No Wifi Connection";
return ipAddress;
}
@SimpleFunction(description = "Returns TRUE if the phone is on Wifi, FALSE otherwise")
public static boolean isConnected() {
ConnectivityManager connectivityManager = (ConnectivityManager) activity.getSystemService("connectivity");
NetworkInfo networkInfo = null;
if (connectivityManager != null) {
networkInfo =
connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
}
return networkInfo == null ? false : networkInfo.isConnected();
}
@SimpleFunction(description = "Establish the secret seed for HOTP generation. " +
"Return the SHA1 of the provided seed, this will be used to contact the " +
"rendezvous server.")
public String setHmacSeedReturnCode(String seed) {
AppInvHTTPD.setHmacKey(seed);
MessageDigest Sha1;
try {
Sha1 = MessageDigest.getInstance("SHA1");
} catch (Exception e) {
Log.e(LOG_TAG, "Exception getting SHA1 Instance", e);
return "";
}
Sha1.update(seed.getBytes());
byte [] result = Sha1.digest();
StringBuffer sb = new StringBuffer(result.length * 2);
Formatter formatter = new Formatter(sb);
for (byte b : result) {
formatter.format("%02x", b);
}
Log.d(LOG_TAG, "Seed = " + seed);
Log.d(LOG_TAG, "Code = " + sb.toString());
return sb.toString();
}
@SimpleFunction(description = "Returns true if we are running in the emulator or USB Connection")
public boolean isDirect() {
Log.d(LOG_TAG, "android.os.Build.VERSION.RELEASE = " + android.os.Build.VERSION.RELEASE);
Log.d(LOG_TAG, "android.os.Build.PRODUCT = " + android.os.Build.PRODUCT);
if (android.os.Build.PRODUCT.contains("google_sdk")) { // Emulator is always direct
return true;
}
if (form instanceof ReplForm) {
return ((ReplForm)form).isDirect();
} else {
return false;
}
}
@SimpleFunction(description = "Start the internal AppInvHTTPD to listen for incoming forms. FOR REPL USE ONLY!")
public void startHTTPD(boolean secure) {
ReplForm.topform.startHTTPD(secure);
}
@SimpleFunction(description = "Declare that we have loaded our initial assets and other assets should come from the sdcard")
public void setAssetsLoaded() {
if (form instanceof ReplForm) {
((ReplForm) form).setAssetsLoaded();
}
}
@SimpleFunction(description = "Causes an Exception, used to debug exception processing.")
public static void doFault() throws Exception {
throw new Exception("doFault called!");
// Thread t = new Thread(new Runnable() { // Cause an exception in a background thread to test bugsense
// public void run() {
// String nonesuch = null;
// String causefault = nonesuch.toString(); // This should cause a null pointer fault.
// }
// });
// t.start();
}
@SimpleFunction(description = "Obtain the Android Application Version")
public String getVersionName() {
try {
PackageInfo pInfo = form.getPackageManager().getPackageInfo(form.getPackageName(), 0);
return (pInfo.versionName);
} catch (NameNotFoundException e) {
Log.e(LOG_TAG, "Exception fetching package name.", e);
return ("");
}
}
@SimpleFunction(description = "Downloads the URL and installs it as an Android Package")
public void installURL(String url) {
PackageInstaller.doPackageInstall(form, url);
}
@SimpleFunction(description = "Really Exit the Application")
public void shutdown() {
form.finish();
System.exit(0); // We cannot be restarted, so we better kill the process
}
/**
* This event is fired when the "settings" menu item is selected (only available in the
* Companion App, defined in ReplForm.java).
*/
@SimpleEvent
public void OnSettings() {
EventDispatcher.dispatchEvent(this, "OnSettings");
}
/**
* Static function called from ReplForm when settings menu item is chosen.
* Triggers the "OnSettings" event iff there is a PhoneStatus component (which
* there will be in the Companion App where this is used).
*/
static void doSettings() {
Log.d(LOG_TAG, "doSettings called.");
if (mainInstance != null) {
mainInstance.OnSettings();
} else {
Log.d(LOG_TAG, "mainStance is null on doSettings");
}
}
public static String intToIp(int i) {
return (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ((i >> 16) & 0xFF) + "." + ((i >>24) & 0xFF);
}
}