Java tutorial
/* * Copyright (C) 2017 Google Inc. * * 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 com.google.gapid.views; import static com.google.gapid.widgets.Widgets.createComposite; import static com.google.gapid.widgets.Widgets.createTreeForViewer; import static org.eclipse.jface.dialogs.IDialogConstants.OK_ID; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.gapid.image.Images; import com.google.gapid.models.Settings; import com.google.gapid.proto.device.Device; import com.google.gapid.proto.pkginfo.PkgInfo; import com.google.gapid.server.GapitPkgInfoProcess; import com.google.gapid.util.Events; import com.google.gapid.util.Loadable.MessageType; import com.google.gapid.util.Messages; import com.google.gapid.widgets.DialogBase; import com.google.gapid.widgets.LoadablePanel; import com.google.gapid.widgets.SearchBox; import com.google.gapid.widgets.Theme; import com.google.gapid.widgets.Widgets; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider; import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.StyledString; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.ImageLoader; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.internal.DPIUtil; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; /** * Dialog to allow the user to pick which application and activity to trace. */ public class ActivityPickerDialog extends DialogBase { protected static final Logger LOG = Logger.getLogger(ActivityPickerDialog.class.getName()); private static final int ICON_SIZE_DIP = 24; private static final int INITIAL_MIN_HEIGHT = 600; private final Settings settings; private final Widgets widgets; private final Device.Instance device; private LoadablePanel<Tree> loading; private TreeViewer tree; private PackageLabelProvider labelProvider; private LocalResourceManager resources; private PkgInfo.PackageList packageList; private Action selected; /** * The selected application and activity. */ public static class Action { public final PkgInfo.Package pkg; public final PkgInfo.Activity activity; public final PkgInfo.Action action; private Action(PkgInfo.Package pkg, PkgInfo.Activity activity, PkgInfo.Action action) { this.pkg = pkg; this.activity = activity; this.action = action; } @Override public String toString() { return action.getName() + ":" + pkg.getName() + "/" + activity.getName(); } public static Action getFor(TreeItem item) { Object data = item.getData(); if (data instanceof PkgInfo.Action) { TreeItem activity = item.getParentItem(); TreeItem pkg = activity.getParentItem(); return new Action((PkgInfo.Package) pkg.getData(), (PkgInfo.Activity) activity.getData(), (PkgInfo.Action) data); } else if (data instanceof PkgInfo.Activity) { TreeItem pkg = item.getParentItem(); PkgInfo.Activity activity = (PkgInfo.Activity) data; PkgInfo.Action action = findLaunchAction(activity); if (action == null && activity.getActionsCount() == 1) { action = activity.getActions(0); } return (action == null) ? null : new Action((PkgInfo.Package) pkg.getData(), activity, action); } else if (data instanceof PkgInfo.Package) { return getFor((PkgInfo.Package) data); } else { return null; } } private static Action getFor(PkgInfo.Package pkg) { for (PkgInfo.Activity activity : pkg.getActivitiesList()) { PkgInfo.Action action = findLaunchAction(activity); if (action != null) { return new Action(pkg, activity, action); } } if (pkg.getActivitiesCount() == 1 && pkg.getActivities(0).getActionsCount() == 1) { return new Action(pkg, pkg.getActivities(0), pkg.getActivities(0).getActions(0)); } return null; } public static PkgInfo.Action findLaunchAction(PkgInfo.Activity activity) { for (PkgInfo.Action action : activity.getActionsList()) { if (action.getIsLaunch()) { return action; } } return null; } } public ActivityPickerDialog(Shell parent, Settings settings, Widgets widgets, Device.Instance device) { super(parent, widgets.theme); this.settings = settings; this.widgets = widgets; this.device = device; } public Action getSelected() { return selected; } @Override public String getTitle() { return Messages.SELECT_ACTIVITY; } @Override public void create() { super.create(); getButton(OK_ID).setEnabled(false); } @Override protected void configureShell(Shell newShell) { super.configureShell(newShell); load(newShell); } @Override protected Point getInitialSize() { Point size = super.getInitialSize(); size.y = Math.max(size.y, INITIAL_MIN_HEIGHT); return size; } @Override protected Control createDialogArea(Composite parent) { Composite area = (Composite) super.createDialogArea(parent); Composite container = createComposite(area, new GridLayout(1, false)); container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); SearchBox search = new SearchBox(container, true); search.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); loading = LoadablePanel.create(container, widgets, p -> createTreeForViewer(p, SWT.BORDER)); loading.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); tree = Widgets.createTreeViewer(loading.getContents()); tree.setContentProvider(new PackageContentProvider()); labelProvider = new PackageLabelProvider(widgets.theme); tree.setLabelProvider(new DelegatingStyledCellLabelProvider(labelProvider)); tree.setComparator(new ViewerComparator()); // Sort by name. tree.getTree().addListener(SWT.Selection, e -> { selected = Action.getFor((TreeItem) e.item); }); resources = new LocalResourceManager(JFaceResources.getResources(), tree.getTree()); update(); search.addListener(Events.Search, e -> { if (e.text.isEmpty()) { tree.resetFilters(); return; } Pattern pattern = SearchBox.getPattern(e.text, (e.detail & Events.REGEX) != 0); tree.setFilters(new ViewerFilter() { @Override public boolean select(Viewer viewer, Object parentElement, Object element) { return !(element instanceof PkgInfo.Package) || pattern.matcher(((PkgInfo.Package) element).getName()).find(); } }); }); return area; } @Override protected void createButtonsForButtonBar(Composite parent) { Button ok = createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true); createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false); ok.setEnabled(false); tree.getTree().addListener(SWT.Selection, e -> ok.setEnabled(selected != null)); } private void load(Shell shell) { float iconDensityScale = DPIUtil.getDeviceZoom() / 100.0f; GapitPkgInfoProcess process = new GapitPkgInfoProcess(settings, device.getSerial(), iconDensityScale); Futures.addCallback(process.start(), new FutureCallback<PkgInfo.PackageList>() { @Override public void onFailure(Throwable t) { LOG.log(Level.WARNING, "Failed to read package info", t); Widgets.scheduleIfNotDisposed(shell, () -> showError(t.getMessage())); } @Override public void onSuccess(PkgInfo.PackageList result) { Widgets.scheduleIfNotDisposed(shell, () -> setPackageList(result)); } }); } protected void setPackageList(PkgInfo.PackageList packageList) { this.packageList = packageList; update(); } protected void showError(String message) { loading.showMessage(MessageType.Error, message); } private void update() { if (packageList == null) { tree.setInput(PkgInfo.PackageList.getDefaultInstance()); loading.startLoading(); return; } int iconSize = (int) (ICON_SIZE_DIP * DPIUtil.getDeviceZoom() / 100.0f); ImageLoader loader = new ImageLoader(); int iconCount = packageList.getIconsCount(); Image icons[] = new Image[iconCount]; for (int i = 0; i < iconCount; i++) { ImageData imageData[] = loader.load(packageList.getIcons(i).newInput()); icons[i] = Images.createNonScaledImage(resources, imageData[0].scaledTo(iconSize, iconSize)); } labelProvider.setIcons(icons); tree.setInput(packageList); loading.stopLoading(); } private static class PackageContentProvider implements ITreeContentProvider { public PackageContentProvider() { } @Override public Object[] getElements(Object root) { return ((PkgInfo.PackageList) root).getPackagesList().toArray(); } @Override public boolean hasChildren(Object element) { if (element instanceof PkgInfo.PackageList) { return ((PkgInfo.PackageList) element).getPackagesCount() > 0; } else if (element instanceof PkgInfo.Package) { return ((PkgInfo.Package) element).getActivitiesCount() > 0; } else if (element instanceof PkgInfo.Activity) { return ((PkgInfo.Activity) element).getActionsCount() > 0; } else { return false; } } @Override public Object[] getChildren(Object element) { if (element instanceof PkgInfo.PackageList) { return ((PkgInfo.PackageList) element).getPackagesList().toArray(); } else if (element instanceof PkgInfo.Package) { return ((PkgInfo.Package) element).getActivitiesList().toArray(); } else if (element instanceof PkgInfo.Activity) { return ((PkgInfo.Activity) element).getActionsList().toArray(); } else { return new Object[0]; } } @Override public Object getParent(Object element) { return null; } } private static class PackageLabelProvider extends LabelProvider implements IStyledLabelProvider { private final Theme theme; private Image[] icons = new Image[0]; public PackageLabelProvider(Theme theme) { this.theme = theme; } public void setIcons(Image[] icons) { this.icons = icons; } @Override public StyledString getStyledText(Object element) { if (element instanceof PkgInfo.Package) { return new StyledString(((PkgInfo.Package) element).getName()); } else if (element instanceof PkgInfo.Activity) { PkgInfo.Activity a = (PkgInfo.Activity) element; return new StyledString(a.getName(), Action.findLaunchAction(a) != null ? theme.labelStyler() : null); } else if (element instanceof PkgInfo.Action) { PkgInfo.Action a = (PkgInfo.Action) element; return new StyledString(a.getName(), a.getIsLaunch() ? theme.labelStyler() : null); } else { return new StyledString(""); } } @Override public Image getImage(Object element) { if (element instanceof PkgInfo.Package) { return getIcon(((PkgInfo.Package) element).getIcon(), true); } else if (element instanceof PkgInfo.Activity) { return getIcon(((PkgInfo.Activity) element).getIcon(), false); } else { return null; } } private Image getIcon(int index, boolean fallback) { if (index < 0 || index >= icons.length || icons[index] == null) { return fallback ? theme.androidLogo() : null; } return icons[index]; } } }