source: trunk/autoquest-androidmonitor/src/main/java/de/ugoe/cs/autoquest/androidmonitor/AndroidMonitorLogFile.java @ 1871

Last change on this file since 1871 was 1871, checked in by funger, 9 years ago
  • improve documentation
  • fix a bug
  • Property svn:mime-type set to text/plain
File size: 20.6 KB
Line 
1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15package de.ugoe.cs.autoquest.androidmonitor;
16
17import java.io.File;
18import java.io.FileNotFoundException;
19import java.io.FileOutputStream;
20import java.io.IOException;
21import java.io.StringWriter;
22import java.util.ArrayList;
23import java.util.List;
24
25import org.xmlpull.v1.XmlSerializer;
26
27import android.app.Activity;
28import android.content.Context;
29import android.content.pm.ApplicationInfo;
30import android.content.pm.PackageManager;
31import android.content.pm.PackageManager.NameNotFoundException;
32import android.util.Log;
33import android.util.Xml;
34import android.view.View;
35import android.view.ViewGroup;
36import android.widget.ImageView;
37import android.widget.TextView;
38
39/**
40 * <p>
41 * Writes informations about the device, the application, the components of the activity and the
42 * events in a log file. File is stored in the space of the application. Path is normally internal
43 * storage: data/data/<package name of the application using the AndroidMonitor>/files/ e.g.
44 * data/data/de.ugoe.cs.autoquest.androidmonitor.testApp/files/
45 * </p>
46 *
47 * @author Florian Unger
48 * @version 1.0
49 */
50public class AndroidMonitorLogFile {
51
52    /**
53     * <p>
54     * Name of the log file which is stored in the internal space of the device.
55     * </p>
56     */
57    private static String name;
58
59    /**
60     * <p>
61     * Name of the Application which is monitored.
62     * </p>
63     */
64    private static String appName;
65
66    /**
67     * <p>
68     * File representation to store monitored information.
69     * </p>
70     */
71    private static File file;
72
73    /**
74     * <p>
75     * List representing all components which was written to log file before.
76     * </p>
77     */
78    private static List<Integer> currentLoggedComponents;
79
80    /**
81     *
82     * <p>
83     * Constructor. Creates a new AndroidmonitorLogFile.
84     * </p>
85     *
86     * @param appName
87     *            Name of the calling application.
88     * @param dir
89     *            Folder to store the log file.
90     */
91    public AndroidMonitorLogFile(Activity activity) {
92        createFile(activity);
93    }
94
95    /**
96     *
97     * <p>
98     * Get file name which is in use.
99     * </p>
100     *
101     * @return filename
102     */
103    public static String getFileName() {
104        return AndroidMonitorLogFile.name;
105    }
106
107    /**
108     * Get application name as defined in Package Name and write it to appName.
109     *
110     * @param pContext
111     *            package context; could also be an activity
112     * @return app name
113     */
114    private String getAppLable(Context pContext) {
115        // source:
116        // http://stackoverflow.com/questions/11229219/android-get-application-name-not-package-name
117        // (last call 2014-09-04)
118        String appLabel;
119        PackageManager lPackageManager = pContext.getPackageManager();
120        ApplicationInfo lApplicationInfo = null;
121        try {
122            lApplicationInfo =
123                lPackageManager.getApplicationInfo(pContext.getApplicationInfo().packageName, 0);
124        }
125        catch (final NameNotFoundException e) {
126           
127        }
128        appLabel = (String) (lApplicationInfo != null ? lPackageManager
129            .getApplicationLabel(lApplicationInfo) : "Unknown");
130        return appLabel;
131    }
132
133    /**
134     *
135     * <p>
136     * Get package Name.
137     * </p>
138     *
139     * @param pContext
140     *            package context; could also be an activity
141     * @return package name
142     */
143    private String getAppPackageName(Context pContext) {
144        return pContext.getPackageName();
145    }
146
147    /**
148     *
149     * <p>
150     * Writes information about the application to the log file.
151     * </p>
152     *
153     * @param activity
154     *
155     */
156    private void setAppInformation(Activity activity) {
157
158        XmlSerializer serializer = Xml.newSerializer();
159        StringWriter writer = new StringWriter();
160        try {
161            serializer.setOutput(writer);
162
163            serializer.startTag("", "application");
164
165            serializer.startTag("", "param");
166            serializer.attribute("", "value", getAppPackageName(activity));
167            serializer.attribute("", "name", "package");
168            serializer.endTag("", "param");
169
170            serializer.startTag("", "param");
171            serializer.attribute("", "value", getAppLable(activity));
172            serializer.attribute("", "name", "name");
173            serializer.endTag("", "param");
174
175            serializer.endTag("", "application");
176            serializer.endDocument();
177
178            writeToFile(writer.toString());
179        }
180        catch (IllegalArgumentException e) {
181            e.printStackTrace();
182            Log.e("file", "outputstream: " + e.getMessage());
183        }
184        catch (IllegalStateException e) {
185            e.printStackTrace();
186            Log.e("file", "outputstream: " + e.getMessage());
187        }
188        catch (IOException e) {
189            e.printStackTrace();
190            Log.e("file", "outputstream: " + e.getMessage());
191        }
192    }
193
194    /**
195     * <p>
196     * Query device information and store it to log file.
197     * </p>
198     *
199     */
200    private void setDeviceInformation() {
201
202        XmlSerializer serializer = Xml.newSerializer();
203        StringWriter writer = new StringWriter();
204        try {
205            serializer.setOutput(writer);
206            serializer.startTag("", "device");
207            serializer.startTag("", "param");
208            serializer.attribute("", "value", "" + android.os.Build.VERSION.SDK_INT);
209            serializer.attribute("", "name", "sdk_version");
210            serializer.endTag("", "param");
211
212            serializer.startTag("", "param");
213            serializer.attribute("", "value", android.os.Build.DEVICE);
214            serializer.attribute("", "name", "device");
215            serializer.endTag("", "param");
216
217            serializer.startTag("", "param");
218            serializer.attribute("", "value", android.os.Build.MANUFACTURER);
219            serializer.attribute("", "name", "manufacturer");
220            serializer.endTag("", "param");
221
222            serializer.startTag("", "param");
223            serializer.attribute("", "value", android.os.Build.MODEL);
224            serializer.attribute("", "name", "model");
225            serializer.endTag("", "param");
226
227            // TODO get resolution ...
228
229            serializer.endTag("", "device");
230            serializer.endDocument();
231
232            writeToFile(writer.toString());
233
234        }
235        catch (IllegalArgumentException e) {
236            e.printStackTrace();
237            Log.e("file", "outputstream: " + e.getMessage());
238        }
239        catch (IllegalStateException e) {
240            e.printStackTrace();
241            Log.e("file", "outputstream: " + e.getMessage());
242        }
243        catch (IOException e) {
244            e.printStackTrace();
245            Log.e("file", "outputstream: " + e.getMessage());
246        }
247
248    }
249
250    /**
251     * <p>
252     * Adds some information of an component of an activity (view) to the file.
253     * </p>
254     *
255     * @param view
256     *            view to be logged
257     * @param parentHash
258     *            hash of the parent view
259     * @param activityName
260     *            name of the activity that is analyzed
261     */
262    public void addComponent(View view, int parentHash, String activityName) {
263        XmlSerializer serializer = Xml.newSerializer();
264        StringWriter writer = new StringWriter();
265
266        try {
267            serializer.setOutput(writer);
268            serializer.startTag("", "component");
269            /*
270             * (non-Javadoc)
271             * TODO find a way in that the hash code is unique over time and target
272             *
273             * view.getId() seems to be unique over time and targets but there is a problem. In some
274             * cases there is no ID (value: -1).
275             *
276             * view.getId() returns different values in case of some identical components.
277             * E.g. in the application timerdroid the id of the list elements
278             * changes when calling home button.
279             */
280            serializer.attribute("", "hash", "" + view.hashCode());
281            currentLoggedComponents.add(view.hashCode());
282
283            /*
284             * (non-Javadoc)
285             * http://developer.android.com/reference/android/view/View.html#getId()
286             * Returns this view's identifier.
287             */
288            serializer.startTag("", "param");
289            serializer.attribute("", "name", "id");
290            serializer.attribute("", "value", "" + view.getId());
291            serializer.endTag("", "param");
292
293            if (view instanceof TextView) {
294                serializer.startTag("", "param");
295                serializer.attribute("", "name", "title");
296                TextView textView = (TextView) view;
297                serializer.attribute("", "value", "" + textView.getText());
298                serializer.endTag("", "param");
299            }
300            //TODO in case of an image add file name
301            if (view instanceof ImageView) {
302                serializer.startTag("", "param");
303                serializer.attribute("", "name", "title");
304                serializer.attribute("", "value", "image:" );
305                serializer.endTag("", "param");
306            }
307
308            serializer.startTag("", "param");
309            serializer.attribute("", "name", "path");
310            serializer.attribute("", "value", activityName + "/" + getViewPath(view) +
311                view.getClass().getSimpleName());
312            serializer.endTag("", "param");
313
314            serializer.startTag("", "param");
315            serializer.attribute("", "name", "class");
316            serializer.attribute("", "value", view.getClass().getName());
317            serializer.endTag("", "param");
318
319            serializer.startTag("", "param");
320            serializer.attribute("", "name", "parent");
321            /*
322             * (non-Javadoc)
323             * Problem in using view.getParent().hashCode():
324             * http://developer.android.com/reference/android/view/View.html#getParent() tells:
325             * "... parent is a ViewParent and not necessarily a View." ViewParent does not have a
326             * method hashCode(). Solution is done add parentHash as parameter to method
327             * addComponent() and Androidmonitor-> addLogListenerToView.
328             */
329            serializer.attribute("", "value", "" + parentHash);
330            serializer.endTag("", "param");
331
332            serializer.startTag("", "ancestors");
333
334            Class<? extends Object> classobject = view.getClass();
335
336            while ((classobject != null)) {
337                serializer.startTag("", "ancestor");
338                serializer.attribute("", "name", classobject.getName());
339                serializer.endTag("", "ancestor");
340                classobject = classobject.getSuperclass();
341            }
342            serializer.endTag("", "ancestors");
343
344            serializer.endTag("", "component");
345            serializer.endDocument();
346
347            writeToFile(writer.toString());
348
349        }
350        catch (IllegalArgumentException e) {
351            e.printStackTrace();
352            Log.e("file", "outputstream: " + e.getMessage());
353        }
354        catch (IllegalStateException e) {
355            e.printStackTrace();
356            Log.e("file", "outputstream: " + e.getMessage());
357        }
358        catch (IOException e) {
359            e.printStackTrace();
360            Log.e("file", "outputstream: " + e.getMessage());
361        }
362
363    }
364
365    /**
366     * <p>
367     * Add an event to the log file
368     * </p>
369     *
370     * @param hash
371     *            hash value of the calling view of the listener
372     * @param type
373     *            the type of listener e.g. textView ...
374     * @param message
375     *            message typed in
376     */
377    public void addEvent(int hash, String type, String message) {
378        XmlSerializer serializer = Xml.newSerializer();
379        StringWriter writer = new StringWriter();
380
381        try {
382            serializer.setOutput(writer);
383
384            serializer.startTag("", "event");
385            serializer.attribute("", "id", type);
386
387            serializer.startTag("", "param");
388            serializer.attribute("", "value", "" + hash);
389            serializer.attribute("", "name", "source");
390            serializer.endTag("", "param");
391
392            serializer.startTag("", "param");
393            serializer.attribute("", "value", message);
394            serializer.attribute("", "name", "message");
395            serializer.endTag("", "param");
396
397            serializer.startTag("", "param");
398            serializer.attribute("", "value", "" + System.currentTimeMillis());
399            serializer.attribute("", "name", "timestamp");
400            serializer.endTag("", "param");
401
402            serializer.endTag("", "event");
403            serializer.endDocument();
404
405            writeToFile(writer.toString());
406        }
407        catch (IllegalArgumentException e) {
408            e.printStackTrace();
409            Log.e("file", "outputstream: " + e.getMessage());
410        }
411        catch (IllegalStateException e) {
412            e.printStackTrace();
413            Log.e("file", "outputstream: " + e.getMessage());
414        }
415        catch (IOException e) {
416            e.printStackTrace();
417            Log.e("file", "outputstream: " + e.getMessage());
418        }
419    }
420
421    /**
422     * <p>
423     * Add an event to the log file
424     * </p>
425     *
426     * @param view
427     *            the calling view of the listener
428     * @param type
429     *            the type of listener e.g. onClick ...
430     */
431    public void addEvent(View view, String type) {
432
433        String x = "" + view.getX();
434        String y = "" + view.getY();
435
436        XmlSerializer serializer = Xml.newSerializer();
437        StringWriter writer = new StringWriter();
438
439        try {
440            serializer.setOutput(writer);
441
442            serializer.startTag("", "event");
443            serializer.attribute("", "id", type);
444
445            serializer.startTag("", "param");
446            serializer.attribute("", "value", x);
447            serializer.attribute("", "name", "X");
448            serializer.endTag("", "param");
449
450            serializer.startTag("", "param");
451            serializer.attribute("", "value", y);
452            serializer.attribute("", "name", "Y");
453            serializer.endTag("", "param");
454
455            serializer.startTag("", "param");
456            serializer.attribute("", "value", "" + view.hashCode());
457            serializer.attribute("", "name", "source");
458            serializer.endTag("", "param");
459
460            serializer.startTag("", "param");
461            serializer.attribute("", "value", "" + System.currentTimeMillis());
462            serializer.attribute("", "name", "timestamp");
463            serializer.endTag("", "param");
464
465            serializer.endTag("", "event");
466            serializer.endDocument();
467
468            writeToFile(writer.toString());
469        }
470        catch (IllegalArgumentException e) {
471            e.printStackTrace();
472            Log.e("file", "outputstream: " + e.getMessage());
473        }
474        catch (IllegalStateException e) {
475            e.printStackTrace();
476            Log.e("file", "outputstream: " + e.getMessage());
477        }
478        catch (IOException e) {
479            e.printStackTrace();
480            Log.e("file", "outputstream: " + e.getMessage());
481        }
482    }
483
484    /**
485     *
486     * <p>
487     * Creates a new file to store information.
488     * </p>
489     *
490     * @param activity
491     *            Calling application.
492     */
493    private void createFile(Activity activity) {
494       
495        AndroidMonitorLogFile.appName = getAppLable(activity);
496        AndroidMonitorLogFile.name =
497            "androidLogFile_" + AndroidMonitorLogFile.appName + System.currentTimeMillis() + ".log";
498        try {
499            AndroidMonitorLogFile.file =
500                new File(activity.getFilesDir(), AndroidMonitorLogFile.name);
501            if (AndroidMonitorLogFile.file.exists() && !AndroidMonitorLogFile.file.isDirectory()) {
502                createFile(activity, 0);               
503            }
504            else {
505                AndroidMonitorLogFile.currentLoggedComponents = new ArrayList<Integer>();
506                writeHeaderToFile(activity);
507               
508            }
509        }
510        catch (Exception e) {
511            e.printStackTrace();
512            Log.e("AndroidMonitorLogFile.file", "file: " + e.getMessage());
513        }
514    }
515
516    /**
517     *
518     * <p>
519     * Creates a new file to store information. Counts up if file exists.
520     * </p>
521     *
522     * @param activity
523     *            Calling application.
524     * @param count
525     *            File number.
526     */
527    private void createFile(Activity activity, int count) {
528        AndroidMonitorLogFile.name =
529            "androidLogFile_" + count + "_" + AndroidMonitorLogFile.appName +
530                System.currentTimeMillis() + ".log";
531       
532        try {
533            AndroidMonitorLogFile.file =
534                new File(activity.getFilesDir(), AndroidMonitorLogFile.name);
535            if (AndroidMonitorLogFile.file.exists() && !AndroidMonitorLogFile.file.isDirectory()) {
536                count++;
537                createFile(activity, count);
538            }
539            else {
540                writeHeaderToFile(activity);
541            }
542        }
543        catch (Exception e) {
544            e.printStackTrace();
545            Log.e("AndroidMonitorLogFile.file", "file: " + e.getMessage());
546        }
547    }
548
549    /**
550     *
551     * <p>
552     * Writes XML head, device and application information to file.
553     * </p>
554     *
555     * @param activity
556     *            Calling application.
557     */
558    private void writeHeaderToFile(Activity activity) {
559        writeToFile("<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><sessions>");
560        setDeviceInformation();
561        setAppInformation(activity);
562    }
563
564    /**
565     * <p>
566     * Writes given information to the file. e.g. previous produced XML statements.
567     * </p>
568     *
569     * @param data
570     *            content to add to the file
571     */
572    private void writeToFile(String data) {
573
574        FileOutputStream outputStream;
575        try {
576            outputStream = new FileOutputStream(AndroidMonitorLogFile.file, true);
577            outputStream.write(data.getBytes());
578            outputStream.close();
579        }
580        catch (FileNotFoundException e) {
581            e.printStackTrace();
582            Log.e("file", "outputstream: " + e.getMessage());
583        }
584        catch (IOException e) {
585            e.printStackTrace();
586            Log.e("file", "outputstream: " + e.getMessage());
587        }
588
589    }
590
591    /**
592     * <p>
593     * Generates the path of an view element.
594     * </p>
595     *
596     * @param view
597     * @return path path to the element
598     */
599    private String getViewPath(View view) {
600        return getViewPath(view, null);
601    }
602
603    /**
604     * <p>
605     * Generates the path of an view element.
606     * </p>
607     *
608     * @param view
609     * @param path
610     * @return path path to the element
611     */
612    private String getViewPath(View view, String path) {
613        if (path == null) {
614            path = "";
615        }
616        else {
617            path = view.getClass().getSimpleName() + "/" + path;
618        }
619        if (view.getParent() != null && (view.getParent() instanceof ViewGroup)) {
620            return getViewPath((View) view.getParent(), path);
621        }
622        else {
623            return path;
624        }
625    }
626
627    /**
628     *
629     * <p>
630     * Check whether a component is still written to log file.
631     * </p>
632     *
633     * @param hashCode
634     *            hash code of the view
635     * @return
636     */
637    public static Boolean isComponentLogged(Integer hashCode) {
638        return AndroidMonitorLogFile.currentLoggedComponents.contains(hashCode);
639    }
640
641}
Note: See TracBrowser for help on using the repository browser.