// Copyright 2012 Georg-August-Universität Göttingen, Germany // // 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 de.ugoe.cs.autoquest.androidmonitor; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; import java.util.List; import org.xmlpull.v1.XmlSerializer; import android.util.Log; import android.util.Xml; import android.view.View; import android.view.ViewGroup; /** *

* TODO comment *

* * @author Florian Unger * @version 1.0 */ public class AndroidMonitorLogFile { /** *

* Name of the log file which is stored in the internal space of the device. *

*/ private String name; /** *

* File representation to store monitored information. *

*/ private File file; /** *

* List representing all components which was written to log file before. *

*/ private List currentLoggedComponents; /** * *

* Constructor. Creates a new AndroidmonitorLogFile. *

* * @param appName * Name of the calling application. * @param dir * Folder to store the log file. */ public AndroidMonitorLogFile(String appName, File dir) { currentLoggedComponents = new ArrayList(); this.name = "androidLogFile_" + appName + System.currentTimeMillis() + ".log"; try { // prove if file exists this.file = new File(dir, this.name); /* * if file does not exists write device and app information to a new * file. Otherwise use existing file and add activity information to * file. */ // TODO prove if file exists and add activity information if (true) { // !this.file.exists() /* * create log file. Using method openFileOutput() does not work * for this project due to the reason that this method would try * to create the file in the directory of the non-existing * directory de.ugoe.cs.androidmonitor. This directory does not * exist due to the reason that this project is a library and * the file has to be stored in the directory of the running * application. Furthermore it would not be possible to write in * another app directory as the own one. */ String string = ""; try { FileOutputStream outputStream = new FileOutputStream( this.file); outputStream.write(string.getBytes()); outputStream.close(); } catch (Exception e) { Log.e("this.file", "outputstream: " + e.getMessage()); } setDeviceInformation(); setAppInformation(); } /*else { // TODO add activity information }*/ } catch (Exception e) { e.printStackTrace(); Log.e("file", "file: " + e.getMessage()); } } /** * *

* Get file name which is in use. *

* * @return filename */ public String getFileName() { return this.name; } /** * *

* Writes information about the application to the log file. *

* */ private void setAppInformation() { // TODO create app information in the same manner as coded in // getDeviceInformation } /** *

* Query device information and store it to log file. *

* */ private void setDeviceInformation() { XmlSerializer serializer = Xml.newSerializer(); StringWriter writer = new StringWriter(); try { serializer.setOutput(writer); serializer.startTag("", "device"); serializer.startTag("", "param"); serializer.attribute("", "value", "" + android.os.Build.VERSION.SDK_INT); serializer.attribute("", "name", "sdk_version"); serializer.endTag("", "param"); serializer.startTag("", "param"); serializer.attribute("", "value", android.os.Build.DEVICE); serializer.attribute("", "name", "device"); serializer.endTag("", "param"); serializer.startTag("", "param"); serializer.attribute("", "value", android.os.Build.MANUFACTURER); serializer.attribute("", "name", "manufacturer"); serializer.endTag("", "param"); serializer.startTag("", "param"); serializer.attribute("", "value", android.os.Build.MODEL); serializer.attribute("", "name", "model"); serializer.endTag("", "param"); // TODO get resolution ... serializer.endTag("", "device"); serializer.endDocument(); writeToFile(writer.toString()); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); Log.e("file", "outputstream: " + e.getMessage()); } catch (IllegalStateException e) { // TODO Auto-generated catch block e.printStackTrace(); Log.e("file", "outputstream: " + e.getMessage()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); Log.e("file", "outputstream: " + e.getMessage()); } } /** *

* Adds some information of an component of an activity (view) to the file. *

* * @param view * view to be logged * @param parentHash * hash of the parent view * @param activityName * name of the activity that is analyzed */ public void addComponent(View view, int parentHash, String activityName) { XmlSerializer serializer = Xml.newSerializer(); StringWriter writer = new StringWriter(); try { serializer.setOutput(writer); serializer.startTag("", "component"); // TODO find a way in that the hash code is unique over time and // target /* * (non-Javadoc) view.getId() seems to be unique over time and * targets but there is a problem. In some cases there is no ID * (value: -1). */ serializer.attribute("", "hash", "" + view.hashCode()); serializer.startTag("", "param"); serializer.attribute("", "name", "id"); serializer.attribute("", "value", "" + view.getId()); serializer.endTag("", "param"); serializer.startTag("", "param"); serializer.attribute("", "name", "path"); serializer.attribute("", "value", activityName + "/" + getViewPath(view) + view.getClass().getSimpleName()); serializer.endTag("", "param"); serializer.startTag("", "param"); serializer.attribute("", "name", "class"); serializer.attribute("", "value", view.getClass().getName()); serializer.endTag("", "param"); serializer.startTag("", "param"); serializer.attribute("", "name", "parent"); // Problem in using view.getParent().hashCode(): // http://developer.android.com/reference/android/view/View.html#getParent() // tells: "... parent is a ViewParent and not necessarily a View." // ViewParent does not have a method hashCode(). Solution is done // add parentHash as parameter to method addComponent() and // Androidmonitor-> addLogListenerToView. serializer.attribute("", "value", "" + parentHash); serializer.endTag("", "param"); // TODO add title e.g. android:text="Button" serializer.startTag("", "ancestors"); Class classobject = view.getClass(); while((classobject != null)){ serializer.startTag("", "ancestor"); serializer.attribute("", "name", classobject.getName()); serializer.endTag("", "ancestor"); classobject = classobject.getSuperclass(); } serializer.endTag("", "ancestors"); serializer.endTag("", "component"); serializer.endDocument(); writeToFile(writer.toString()); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); Log.e("file", "outputstream: " + e.getMessage()); } catch (IllegalStateException e) { // TODO Auto-generated catch block e.printStackTrace(); Log.e("file", "outputstream: " + e.getMessage()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); Log.e("file", "outputstream: " + e.getMessage()); } } /** *

* Add an event to the log file *

* * @param hash * hash value of the calling view of the listener * @param type * the type of listener e.g. textView ... * @param message * message typed in */ public void addEvent(int hash, String type, String message){ XmlSerializer serializer = Xml.newSerializer(); StringWriter writer = new StringWriter(); try { serializer.setOutput(writer); serializer.startTag("", "event"); serializer.attribute("", "id", type); serializer.startTag("", "param"); serializer.attribute("", "value", "" + hash); serializer.attribute("", "name", "source"); serializer.endTag("", "param"); serializer.startTag("", "param"); serializer.attribute("", "value", message); serializer.attribute("", "name", "message"); serializer.endTag("", "param"); serializer.startTag("", "param"); serializer.attribute("", "value", "" + System.currentTimeMillis()); serializer.attribute("", "name", "timestamp"); serializer.endTag("", "param"); serializer.endTag("", "event"); serializer.endDocument(); writeToFile(writer.toString()); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); Log.e("file", "outputstream: " + e.getMessage()); } catch (IllegalStateException e) { // TODO Auto-generated catch block e.printStackTrace(); Log.e("file", "outputstream: " + e.getMessage()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); Log.e("file", "outputstream: " + e.getMessage()); } } /** *

* Add an event to the log file *

* * @param view * the calling view of the listener * @param type * the type of listener e.g. onClick ... */ public void addEvent(View view, String type) { String x = "" + view.getX(); String y = "" + view.getY(); XmlSerializer serializer = Xml.newSerializer(); StringWriter writer = new StringWriter(); try { serializer.setOutput(writer); serializer.startTag("", "event"); serializer.attribute("", "id", type); serializer.startTag("", "param"); serializer.attribute("", "value", x); serializer.attribute("", "name", "X"); serializer.endTag("", "param"); serializer.startTag("", "param"); serializer.attribute("", "value", y); serializer.attribute("", "name", "Y"); serializer.endTag("", "param"); serializer.startTag("", "param"); serializer.attribute("", "value", "" + view.hashCode()); serializer.attribute("", "name", "source"); serializer.endTag("", "param"); serializer.startTag("", "param"); serializer.attribute("", "value", "" + System.currentTimeMillis()); serializer.attribute("", "name", "timestamp"); serializer.endTag("", "param"); serializer.endTag("", "event"); serializer.endDocument(); writeToFile(writer.toString()); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); Log.e("file", "outputstream: " + e.getMessage()); } catch (IllegalStateException e) { // TODO Auto-generated catch block e.printStackTrace(); Log.e("file", "outputstream: " + e.getMessage()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); Log.e("file", "outputstream: " + e.getMessage()); } } /** *

* Writes given information to the file. e.g. previous produced XML * statements. *

* * @param data * content to add to the file */ private void writeToFile(String data) { FileOutputStream outputStream; try { outputStream = new FileOutputStream(file, true); outputStream.write(data.getBytes()); outputStream.close(); } catch (FileNotFoundException e) { e.printStackTrace(); Log.e("file", "outputstream: " + e.getMessage()); } catch (IOException e) { e.printStackTrace(); Log.e("file", "outputstream: " + e.getMessage()); } } /** *

* Generates the path of an view element. *

* * @param view * @return path path to the element */ private String getViewPath(View view) { return getViewPath(view, null); } /** *

* Generates the path of an view element. *

* * @param view * @param path * @return path path to the element */ private String getViewPath(View view, String path) { if (path == null) { path = ""; } else { path = view.getClass().getSimpleName() + "/" + path; } if (view.getParent() != null && (view.getParent() instanceof ViewGroup)) { return getViewPath((View) view.getParent(), path); } else { return path; } } /** * *

* Check whether a component is still written to log file. *

* * @param hashCode * hash code of the view * @return */ public Boolean isComponentLogged(Integer hashCode){ return currentLoggedComponents.contains(hashCode); } }