AI2 Component  (Version nb184)
PhoneStatus.java
Go to the documentation of this file.
1 // -*- mode: java; c-basic-offset: 2; -*-
2 // Copyright 2011-2020 MIT, All rights reserved
3 // Released under the Apache License, Version 2.0
4 // http://www.apache.org/licenses/LICENSE-2.0
5 
6 package com.google.appinventor.components.runtime;
7 
8 import android.app.Activity;
9 
10 import android.content.Context;
11 import android.content.Intent;
12 
13 import android.content.pm.PackageManager.NameNotFoundException;
14 
15 import android.net.ConnectivityManager;
16 import android.net.DhcpInfo;
17 import android.net.NetworkInfo;
18 import android.net.Uri;
19 import android.net.wifi.WifiManager;
20 
21 import android.os.Build;
22 
23 import android.util.Log;
24 
34 
38 
45 
46 import java.lang.reflect.InvocationTargetException;
47 import java.lang.reflect.Method;
48 import java.security.MessageDigest;
49 
50 import java.util.Formatter;
51 
52 
61 @DesignerComponent(version = YaVersion.PHONESTATUS_COMPONENT_VERSION,
62  description = "Component that returns information about the phone.",
63  category = ComponentCategory.INTERNAL,
64  nonVisible = true,
65  iconName = "images/phoneip.png")
66 @SimpleObject
67 @UsesLibraries(libraries = "webrtc.jar," +
68  "google-http-client-beta.jar," +
69  "google-http-client-android2-beta.jar," +
70  "google-http-client-android3-beta.jar")
71 @UsesNativeLibraries(v7aLibraries = "libjingle_peerconnection_so.so",
72  v8aLibraries = "libjingle_peerconnection_so.so",
73  x86_64Libraries = "libjingle_peerconnection_so.so")
74 public class PhoneStatus extends AndroidNonvisibleComponent implements Component {
75 
76  private static Activity activity;
77  private static final String LOG_TAG = "PhoneStatus";
78  private final Form form;
79  private static PhoneStatus mainInstance = null;
80  private static boolean useWebRTC = false;
81  private String firstSeed = null;
82  private String firstHmacSeed = null;
83 
84  public PhoneStatus(ComponentContainer container) {
85  super(container.$form());
86  this.form = container.$form();
87  activity = container.$context();
88  if (mainInstance == null) { // First one?
89  mainInstance = this;
90  }
91  }
92 
93  @SimpleFunction(description = "Returns the IP address of the phone in the form of a String")
94  public static String GetWifiIpAddress() {
95  DhcpInfo ip;
96  Object wifiManager = activity.getSystemService("wifi");
97  ip = ((WifiManager) wifiManager).getDhcpInfo();
98  int s_ipAddress= ip.ipAddress;
99  String ipAddress;
100  if (isConnected())
101  ipAddress = intToIp(s_ipAddress);
102  else
103  ipAddress = "Error: No Wifi Connection";
104  return ipAddress;
105  }
106 
107  @SimpleFunction(description = "Returns TRUE if the phone is on Wifi, FALSE otherwise")
108  public static boolean isConnected() {
109  ConnectivityManager connectivityManager = (ConnectivityManager) activity.getSystemService("connectivity");
110  NetworkInfo networkInfo = null;
111  if (connectivityManager != null) {
112  networkInfo =
113  connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
114  }
115  return networkInfo == null ? false : networkInfo.isConnected();
116  }
117 
118  @SimpleFunction(description = "Establish the secret seed for HOTP generation. " +
119  "Return the SHA1 of the provided seed, this will be used to contact the " +
120  "rendezvous server. Note: This code also starts the connection negotiation " +
121  "process if we are using WebRTC. This is a bit of a kludge...")
122  public String setHmacSeedReturnCode(String seed, String rendezvousServer) {
123 
124  /* If we get an empty seed, just ignore it. */
125  if (seed.equals("")) {
126  return "";
127  }
128 
129  /*
130  * Check to see if we are being re-entered.
131  *
132  * The Companion's design is to setup communications with
133  * the user's browser and get to work. Once this process starts,
134  * enough things are in motion that it is best to quit the
135  * Companion and start a fresh copy if a different code is needed.
136  *
137  * If the same code is entered more then once, we just ignore the
138  * second attempt. This often happens when someone scans a QR Code
139  * and then presses the "Connect" Button because they do not know
140  * that they don't have to do that. Effectively we are "de-bouncing"
141  * the button.
142  *
143  */
144  if (firstSeed != null) { // Hmm. We've been here before!
145  if (!firstSeed.equals(seed)) {
146  // Attempting to use a different seed (code)
147  // Provide a warning dialog box
149  "You cannot use two codes with one start up of the Companion. You should restart the " +
150  "Companion and try again.",
151  "Warning", "OK", new Runnable() {
152  @Override public void run() {
153  // We are going to die here, so the user has to start a new copy. This isn't ideal. A more
154  // correct solution would be to gracefully shutdown the connection process and restart it with
155  // the new seed.
156  form.finish();
157  System.exit(0); // Truly ugly...
158  }
159  });
160  }
161  return firstHmacSeed;
162  }
163 
164  firstSeed = seed;
165 
166  /*
167  * Set the HMAC seed, but only if we are doing the legacy HTTP
168  * thing. Note: Currently we *always* start the HTTP Daemon, even
169  * in WebRTC mode By not setting the seed, we ensure that the HTTP
170  * Daemon cannot accept any blocks
171  *
172  */
173 
174  if (!useWebRTC) {
175  AppInvHTTPD.setHmacKey(seed);
176  }
177 
178  MessageDigest Sha1;
179  try {
180  Sha1 = MessageDigest.getInstance("SHA1");
181  } catch (Exception e) {
182  Log.e(LOG_TAG, "Exception getting SHA1 Instance", e);
183  return "";
184  }
185  Sha1.update(seed.getBytes());
186  byte [] result = Sha1.digest();
187  StringBuffer sb = new StringBuffer(result.length * 2);
188  Formatter formatter = new Formatter(sb);
189  for (byte b : result) {
190  formatter.format("%02x", b);
191  }
192  Log.d(LOG_TAG, "Seed = " + seed);
193  Log.d(LOG_TAG, "Code = " + sb.toString());
194  firstHmacSeed = sb.toString();
195  return firstHmacSeed;
196  }
197 
198  @SimpleFunction(description = "Returns true if we are running in the emulator or USB Connection")
199  public boolean isDirect() {
200  Log.d(LOG_TAG, "android.os.Build.VERSION.RELEASE = " + android.os.Build.VERSION.RELEASE);
201  Log.d(LOG_TAG, "android.os.Build.PRODUCT = " + android.os.Build.PRODUCT);
202  if (ReplForm.isEmulator()) { // Emulator is always direct
203  return true;
204  }
205  if (form instanceof ReplForm) {
206  return ((ReplForm)form).isDirect();
207  } else {
208  return false;
209  }
210  }
211 
212  @SimpleFunction(description = "Start the WebRTC engine")
213  public void startWebRTC(String rendezvousServer, String iceServers) {
214  if (!useWebRTC) {
215  return;
216  }
217  WebRTCNativeMgr webRTCNativeMgr = new WebRTCNativeMgr(rendezvousServer, iceServers);
218  webRTCNativeMgr.initiate((ReplForm) form, (Context)activity, firstSeed);
219  ((ReplForm)form).setWebRTCMgr(webRTCNativeMgr);
220  }
221 
222  @SimpleFunction(description = "Start the internal AppInvHTTPD to listen for incoming forms. FOR REPL USE ONLY!")
223  public void startHTTPD(boolean secure) {
224  if (form.isRepl()) {
225  ((ReplForm) form).startHTTPD(secure);
226  }
227  }
228 
229  @SimpleFunction(description = "Declare that we have loaded our initial assets and other assets should come from the sdcard")
230  public void setAssetsLoaded() {
231  if (form instanceof ReplForm) {
232  ((ReplForm) form).setAssetsLoaded();
233  }
234  }
235 
236  @SimpleFunction(description = "Causes an Exception, used to debug exception processing.")
237  public static void doFault() throws Exception {
238  throw new Exception("doFault called!");
239  // Thread t = new Thread(new Runnable() { // Cause an exception in a background thread to test bugsense
240  // public void run() {
241  // String nonesuch = null;
242  // String causefault = nonesuch.toString(); // This should cause a null pointer fault.
243  // }
244  // });
245  // t.start();
246  }
247 
248  @SimpleFunction(description = "Downloads the URL and installs it as an Android Package via the installed browser")
249  public void installURL(String url) {
250  try {
251  Class<?> clazz = Class.forName("edu.mit.appinventor.companionextras.CompanionExtras");
252  Object o = clazz.getConstructor(Form.class).newInstance(form);
253  Method m = clazz.getMethod("Extra1", String.class);
254  m.invoke(o, url);
255  } catch (Exception e) {
256  // Fall back to using the browser
257  Uri uri = Uri.parse(url + "?store=1");
258  Intent intent = new Intent(Intent.ACTION_VIEW).setData(uri);
259  form.startActivity(intent);
260  }
261  }
262 
263  @SimpleFunction(description = "Really Exit the Application")
264  public void shutdown() {
265  form.finish();
266  System.exit(0); // We cannot be restarted, so we better kill the process
267  }
268 
273  @SimpleEvent
274  public void OnSettings() {
275  EventDispatcher.dispatchEvent(this, "OnSettings");
276  }
277 
284  @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN, defaultValue = "False")
285  @SimpleProperty()
286  public void WebRTC(boolean useWebRTC) {
287  this.useWebRTC = useWebRTC;
288  }
289 
290  @SimpleProperty(category = PropertyCategory.BEHAVIOR, description = "If True we are using WebRTC to talk to the server.")
291  public boolean WebRTC() {
292  return useWebRTC;
293  }
294 
305  @SimpleFunction(description = "Get the current Android SDK Level")
306  public int SdkLevel() {
307  return SdkLevel.getLevel();
308  }
309 
325  @SimpleFunction(description = "Return the our VersionName property")
326  public String GetVersionName() {
327  try {
328  String packageName = form.getPackageName();
329  return form.getPackageManager().getPackageInfo(packageName, 0).versionName;
330  } catch (NameNotFoundException e) {
331  Log.e(LOG_TAG, "Unable to get VersionName", e);
332  return "UNKNOWN";
333  }
334  }
335 
336  @SimpleFunction(description = "Return the app that installed us")
337  public String GetInstaller() {
339  String installer = EclairUtil.getInstallerPackageName("edu.mit.appinventor.aicompanion3", form);
340  if (installer == null) {
341  return "sideloaded";
342  } else {
343  return installer;
344  }
345  } else {
346  return "unknown";
347  }
348  }
349 
350  @SimpleFunction(description = "Return the ACRA Installation ID")
351  public String InstallationId() {
352  return org.acra.util.Installation.id(form);
353  }
354 
355  /* Static context way to get the useWebRTC flag */
356  public static boolean getUseWebRTC() {
357  return useWebRTC;
358  }
359 
365  static void doSettings() {
366  Log.d(LOG_TAG, "doSettings called.");
367  if (mainInstance != null) {
368  mainInstance.OnSettings();
369  } else {
370  Log.d(LOG_TAG, "mainStance is null on doSettings");
371  }
372  }
373 
374  public static String intToIp(int i) {
375  return (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ((i >> 16) & 0xFF) + "." + ((i >>24) & 0xFF);
376  }
377 }
com.google.appinventor.components.runtime.EventDispatcher
Definition: EventDispatcher.java:22
com.google.appinventor.components.runtime.ReplForm
Definition: ReplForm.java:62
com.google.appinventor.components.annotations.SimpleFunction
Definition: SimpleFunction.java:23
com.google.appinventor.components.annotations.UsesLibraries
Definition: UsesLibraries.java:21
com.google.appinventor.components.runtime.util
-*- mode: java; c-basic-offset: 2; -*-
Definition: AccountChooser.java:7
com.google.appinventor.components.common.YaVersion
Definition: YaVersion.java:14
com.google.appinventor.components.annotations.DesignerProperty
Definition: DesignerProperty.java:25
com.google.appinventor.components.runtime.PhoneStatus.getUseWebRTC
static boolean getUseWebRTC()
Definition: PhoneStatus.java:356
com.google.appinventor.components.annotations.UsesNativeLibraries
Definition: UsesNativeLibraries.java:21
com.google.appinventor.components
com.google.appinventor.components.runtime.util.AppInvHTTPD
Definition: AppInvHTTPD.java:41
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN
static final String PROPERTY_TYPE_BOOLEAN
Definition: PropertyTypeConstants.java:35
com.google.appinventor.components.runtime.PhoneStatus.PhoneStatus
PhoneStatus(ComponentContainer container)
Definition: PhoneStatus.java:84
com.google.appinventor.components.runtime.PhoneStatus.OnSettings
void OnSettings()
Definition: PhoneStatus.java:274
com.google.appinventor.components.annotations.DesignerComponent
Definition: DesignerComponent.java:22
com.google.appinventor.components.annotations.SimpleEvent
Definition: SimpleEvent.java:20
com.google.appinventor.components.annotations.PropertyCategory.BEHAVIOR
BEHAVIOR
Definition: PropertyCategory.java:15
com.google.appinventor.components.runtime.AppInventorCompatActivity.isEmulator
static boolean isEmulator()
Definition: AppInventorCompatActivity.java:220
com.google.appinventor.components.runtime.util.SdkLevel.LEVEL_ECLAIR
static final int LEVEL_ECLAIR
Definition: SdkLevel.java:22
com.google.appinventor.components.runtime.Notifier
Definition: Notifier.java:78
com.google.appinventor.components.runtime.AppInventorCompatActivity.isRepl
boolean isRepl()
Definition: AppInventorCompatActivity.java:263
com.google.appinventor.components.runtime.EventDispatcher.dispatchEvent
static boolean dispatchEvent(Component component, String eventName, Object...args)
Definition: EventDispatcher.java:188
com.google.appinventor.components.runtime.AndroidNonvisibleComponent
Definition: AndroidNonvisibleComponent.java:17
com.google.appinventor.components.runtime.util.SdkLevel
Definition: SdkLevel.java:19
com.google.appinventor.components.annotations.SimpleProperty
Definition: SimpleProperty.java:23
com.google.appinventor.components.annotations.PropertyCategory
Definition: PropertyCategory.java:13
com.google.appinventor.components.runtime.ComponentContainer
Definition: ComponentContainer.java:16
com.google.appinventor.components.runtime.util.SdkLevel.getLevel
static int getLevel()
Definition: SdkLevel.java:45
com.google.appinventor.components.runtime
Copyright 2009-2011 Google, All Rights reserved.
Definition: AccelerometerSensor.java:8
com.google.appinventor.components.runtime.Component
Definition: Component.java:17
com.google.appinventor.components.runtime.util.EclairUtil.getInstallerPackageName
static String getInstallerPackageName(String pname, Activity form)
Definition: EclairUtil.java:105
com.google.appinventor.components.runtime.util.WebRTCNativeMgr
Definition: WebRTCNativeMgr.java:65
com.google.appinventor.components.runtime.util.WebRTCNativeMgr.initiate
void initiate(ReplForm form, Context context, String code)
Definition: WebRTCNativeMgr.java:271
com.google.appinventor.components.common
Definition: ComponentCategory.java:7
com.google.appinventor.components.runtime.util.AppInvHTTPD.setHmacKey
static void setHmacKey(String inputKey)
Definition: AppInvHTTPD.java:401
com.google.appinventor.components.common.ComponentCategory
Definition: ComponentCategory.java:48
com.google.appinventor.components.annotations.SimpleObject
Definition: SimpleObject.java:23
com.google
com
com.google.appinventor.components.runtime.ComponentContainer.$form
Form $form()
com.google.appinventor.components.runtime.ComponentContainer.$context
Activity $context()
com.google.appinventor.components.runtime.Notifier.oneButtonAlert
static void oneButtonAlert(Activity activity, String message, String title, String buttonText, final Runnable callBack)
Definition: Notifier.java:164
com.google.appinventor.components.runtime.PhoneStatus.intToIp
static String intToIp(int i)
Definition: PhoneStatus.java:374
com.google.appinventor.components.runtime.util.EclairUtil
Definition: EclairUtil.java:31
com.google.appinventor.components.runtime.Form
Definition: Form.java:126
com.google.appinventor.components.common.PropertyTypeConstants
Definition: PropertyTypeConstants.java:14
com.google.appinventor.components.annotations
com.google.appinventor
com.google.appinventor.components.runtime.PhoneStatus
Definition: PhoneStatus.java:74