// 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.usability; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import de.ugoe.cs.autoquest.eventcore.IEventType; import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonInteraction; import de.ugoe.cs.autoquest.eventcore.gui.MouseClick; import de.ugoe.cs.autoquest.eventcore.gui.MouseDoubleClick; import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement; import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIView; import de.ugoe.cs.autoquest.eventcore.guimodel.IImage; import de.ugoe.cs.autoquest.eventcore.guimodel.IText; import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor; import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel; import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession; /** * TODO comment * * @version $Revision: $ $Date: 16.07.2012$ * @author 2012, last modified by $Author: pharms$ */ public class MisleadingClickCueRule implements UsabilityEvaluationRule { /* * (non-Javadoc) * * @see de.ugoe.cs.usability.UsabilityEvaluationRule#evaluate(TaskTree) */ @Override public UsabilityEvaluationResult evaluate(ITaskModel taskModel) { UselessClickStatistics statistics = new UselessClickStatistics(); int allObserved = calculateStatistics(taskModel.getUserSessions(), statistics); UsabilityEvaluationResult results = new UsabilityEvaluationResult(taskModel); analyzeStatistics(statistics, allObserved, results); return results; } /** * */ private void analyzeStatistics(UselessClickStatistics statistics, int allObserved, UsabilityEvaluationResult results) { for (Map.Entry> uselessClickCounter : statistics.getUselessClickCounters().entrySet()) { for (Map.Entry counter : uselessClickCounter.getValue().entrySet()) { int uselessClicks = counter.getValue(); int noOfViewDisplays = statistics.getViewOpenedCount(uselessClickCounter.getKey()); int ratio = Math.min(1000, 1000 * uselessClicks / noOfViewDisplays); UsabilitySmellIntensity intensity = UsabilitySmellIntensity.getIntensity(ratio, uselessClicks, -1); if (intensity != null) { Map parameters = new HashMap(); parameters.put("noOfViewDisplays", noOfViewDisplays); parameters.put("uselessClicks", uselessClicks); parameters.put("element", counter.getKey()); parameters.put("view", uselessClickCounter.getKey()); results.addSmell (intensity, UsabilitySmellDescription.MISLEADING_CLICK_CUE, parameters); } } } } /** * */ private int calculateStatistics(List sessions, final UselessClickStatistics statistics) { final List leafNodes = new ArrayList<>(); final IGUIView[] currentView = new IGUIView[1]; for (IUserSession session : sessions) { currentView[0] = null; for (final ITaskInstance currentRoot : session) { currentRoot.accept(new DefaultTaskInstanceTraversingVisitor() { @Override public void visit(IEventTaskInstance eventTaskInstance) { leafNodes.add(eventTaskInstance); if (eventTaskInstance.getEvent().getTarget() instanceof IGUIElement) { IGUIView view = ((IGUIElement) eventTaskInstance.getEvent().getTarget()).getView(); if (((currentView[0] == null) && (view != null)) || ((currentView[0] != null) && (!currentView[0].equals(view)))) { currentView[0] = view; statistics.addViewOpened(view); } if (isUselessMouseClick(eventTaskInstance)) { statistics.addUselessClick(eventTaskInstance); } } } }); } } return leafNodes.size(); } /** * */ private boolean isUselessMouseClick(IEventTaskInstance eventTaskInstance) { IEventType type = eventTaskInstance.getEvent().getType(); if ((!(type instanceof MouseClick)) && (!(type instanceof MouseDoubleClick))) { return false; } if (((MouseButtonInteraction) type).getButton() != MouseButtonInteraction.Button.LEFT) { return false; } IGUIElement target = (IGUIElement) eventTaskInstance.getEvent().getTarget(); if ((target instanceof IImage) /*|| (target instanceof IPanel) || (target instanceof IProgressBar) || (target instanceof IScrollPane) || (target instanceof IShape) || (target instanceof ITable)*/ || (target instanceof IText)) { return true; } else { return false; } } /** * */ private class UselessClickStatistics { /** */ private Map viewOpenedCounters = new HashMap<>(); /** */ private Map> uselessClickCounters = new HashMap<>(); /** *

* TODO: comment *

* * @param view */ private void addViewOpened(IGUIView view) { Integer counter = viewOpenedCounters.get(view); if (counter == null) { viewOpenedCounters.put(view, 1); } else { viewOpenedCounters.put(view, counter + 1); } } /** *

* TODO: comment *

* * @param eventTaskInstance */ private void addUselessClick(IEventTaskInstance eventTaskInstance) { Map counterMap = uselessClickCounters.get (((IGUIElement) eventTaskInstance.getEvent().getTarget()).getView()); if (counterMap == null) { counterMap = new HashMap<>(); uselessClickCounters.put (((IGUIElement) eventTaskInstance.getEvent().getTarget()).getView(), counterMap); } Integer counter = counterMap.get(eventTaskInstance.getEvent().getTarget()); if (counter == null) { counterMap.put((IGUIElement) eventTaskInstance.getEvent().getTarget(), 1); } else { counterMap.put((IGUIElement) eventTaskInstance.getEvent().getTarget(), counter + 1); } } /** * */ private Map> getUselessClickCounters() { return uselessClickCounters; } /** * */ private int getViewOpenedCount(IGUIView view) { Integer counter = viewOpenedCounters.get(view); if (counter == null) { return 0; } else { return counter; } } } }