mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-16 09:47:42 +00:00
Merge pull request #5155 from wschaeferB/5423-DiscoveryThumbnailViewer
5423 discovery thumbnail viewer
This commit is contained in:
commit
11296bbbd3
@ -117,6 +117,10 @@ FileSorter.SortingMethod.filetype.displayName=By file type
|
||||
FileSorter.SortingMethod.frequency.displayName=By central repo frequency
|
||||
FileSorter.SortingMethod.keywordlist.displayName=By keyword list names
|
||||
FileSorter.SortingMethod.parent.displayName=By parent path
|
||||
# {0} - numberOfInstances
|
||||
ImageThumbnailPanel.countLabel.text=Number of Instances: {0}
|
||||
# {0} - fileSize
|
||||
ImageThumbnailPanel.sizeLabel.text=Size: {0} bytes
|
||||
ResultsDialog.dialogTitle.text=File search results
|
||||
ResultsDialog.exitButton.text=Exit
|
||||
ResultsDialog.searchButton.text=Run another search
|
||||
|
@ -116,7 +116,7 @@ class FileSearch {
|
||||
|
||||
// Sort and group the results
|
||||
searchResults.sortGroupsAndFiles();
|
||||
LinkedHashMap<String, List<ResultFile>> resultHashMap = searchResults.toLinkedHashMap();
|
||||
Map<String, List<ResultFile>> resultHashMap = searchResults.toLinkedHashMap();
|
||||
for (String groupName : resultHashMap.keySet()) {
|
||||
groupCache.put(groupName, resultHashMap.get(groupName));
|
||||
}
|
||||
@ -251,7 +251,7 @@ class FileSearch {
|
||||
// Collect everything in the search results
|
||||
SearchResults searchResults = new SearchResults(groupSortingType, groupAttributeType, fileSortingMethod);
|
||||
searchResults.add(resultFiles);
|
||||
LinkedHashMap<String, List<ResultFile>> resultHashMap = searchResults.toLinkedHashMap();
|
||||
Map<String, List<ResultFile>> resultHashMap = searchResults.toLinkedHashMap();
|
||||
for (String groupName : resultHashMap.keySet()) {
|
||||
groupCache.put(groupName, resultHashMap.get(groupName));
|
||||
}
|
||||
|
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" max="-2" attributes="0">
|
||||
<Component id="thumbnailPanel" max="32767" attributes="0"/>
|
||||
<Component id="countLabel" alignment="0" pref="201" max="32767" attributes="0"/>
|
||||
<Component id="fileSizeLabel" alignment="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="thumbnailPanel" min="-2" pref="178" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="fileSizeLabel" min="-2" pref="16" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="countLabel" min="-2" pref="12" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="thumbnailPanel">
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="thumbnailLabel">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JLabel" name="fileSizeLabel">
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="countLabel">
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Autopsy
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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 org.sleuthkit.autopsy.filequery;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.ListCellRenderer;
|
||||
import org.openide.util.NbBundle;
|
||||
|
||||
/**
|
||||
* Class which displays a thumbnail and information for an image file.
|
||||
*/
|
||||
public class ImageThumbnailPanel extends javax.swing.JPanel implements ListCellRenderer<ImageThumbnailWrapper> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Color SELECTION_COLOR = new Color(0, 120, 215);
|
||||
|
||||
/**
|
||||
* Creates new form ImageThumbnailPanel
|
||||
*/
|
||||
public ImageThumbnailPanel() {
|
||||
initComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
javax.swing.JPanel thumbnailPanel = new javax.swing.JPanel();
|
||||
thumbnailLabel = new javax.swing.JLabel();
|
||||
fileSizeLabel = new javax.swing.JLabel();
|
||||
countLabel = new javax.swing.JLabel();
|
||||
|
||||
thumbnailPanel.setLayout(new java.awt.GridBagLayout());
|
||||
thumbnailPanel.add(thumbnailLabel, new java.awt.GridBagConstraints());
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
|
||||
.addComponent(thumbnailPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(countLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 201, Short.MAX_VALUE)
|
||||
.addComponent(fileSizeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(thumbnailPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 178, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(fileSizeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(countLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 12, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap())
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JLabel countLabel;
|
||||
private javax.swing.JLabel fileSizeLabel;
|
||||
private javax.swing.JLabel thumbnailLabel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
@NbBundle.Messages({"# {0} - fileSize",
|
||||
"ImageThumbnailPanel.sizeLabel.text=Size: {0} bytes",
|
||||
"# {0} - numberOfInstances",
|
||||
"ImageThumbnailPanel.countLabel.text=Number of Instances: {0}"})
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList<? extends ImageThumbnailWrapper> list, ImageThumbnailWrapper value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
fileSizeLabel.setText(Bundle.ImageThumbnailPanel_sizeLabel_text(value.getResultFile().getFirstInstance().getSize()));
|
||||
countLabel.setText(Bundle.ImageThumbnailPanel_countLabel_text(value.getResultFile().getAllInstances().size()));
|
||||
thumbnailLabel.setIcon(new ImageIcon(value.getThumbnail()));
|
||||
setBackground(isSelected ? SELECTION_COLOR : list.getBackground());
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.6" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,-117,0,0,1,-22"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="thumbnailListScrollPane">
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JList" name="thumbnailList">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="thumbnailListModel" type="code"/>
|
||||
</Property>
|
||||
<Property name="selectionMode" type="int" value="0"/>
|
||||
<Property name="cellRenderer" type="javax.swing.ListCellRenderer" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="new ImageThumbnailPanel()" type="code"/>
|
||||
</Property>
|
||||
<Property name="layoutOrientation" type="int" value="2"/>
|
||||
<Property name="visibleRowCount" type="int" value="0"/>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<ImageThumbnailWrapper>"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Autopsy
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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 org.sleuthkit.autopsy.filequery;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.swing.DefaultListModel;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
|
||||
/**
|
||||
* A JPanel to display image thumbnails.
|
||||
*
|
||||
*/
|
||||
public class ImageThumbnailViewer extends javax.swing.JPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final DefaultListModel<ImageThumbnailWrapper> thumbnailListModel = new DefaultListModel<>();
|
||||
|
||||
/**
|
||||
* Creates new form ImageThumbnailViewer
|
||||
*/
|
||||
public ImageThumbnailViewer() {
|
||||
initComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
javax.swing.JScrollPane thumbnailListScrollPane = new javax.swing.JScrollPane();
|
||||
thumbnailList = new javax.swing.JList<>();
|
||||
|
||||
setLayout(new java.awt.BorderLayout());
|
||||
|
||||
thumbnailList.setModel(thumbnailListModel);
|
||||
thumbnailList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
|
||||
thumbnailList.setCellRenderer(new ImageThumbnailPanel());
|
||||
thumbnailList.setLayoutOrientation(javax.swing.JList.HORIZONTAL_WRAP);
|
||||
thumbnailList.setVisibleRowCount(0);
|
||||
thumbnailListScrollPane.setViewportView(thumbnailList);
|
||||
|
||||
add(thumbnailListScrollPane, java.awt.BorderLayout.CENTER);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JList<ImageThumbnailWrapper> thumbnailList;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
/**
|
||||
* Add a selection listener to the list of thumbnails being displayed.
|
||||
*
|
||||
* @param listener The ListSelectionListener to add to the selection model.
|
||||
*/
|
||||
void addListSelectionListener(ListSelectionListener listener) {
|
||||
thumbnailList.getSelectionModel().addListSelectionListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of AbstractFiles which are represented by the selected image
|
||||
* thumbnail.
|
||||
*
|
||||
* @return The list of AbstractFiles which are represented by the selected image
|
||||
* thumbnail.
|
||||
*/
|
||||
List<AbstractFile> getInstancesForSelected() {
|
||||
synchronized (this) {
|
||||
if (thumbnailList.getSelectedIndex() == -1) {
|
||||
return new ArrayList<>();
|
||||
} else {
|
||||
return thumbnailListModel.getElementAt(thumbnailList.getSelectedIndex()).getResultFile().getAllInstances();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the list of thumbnails being displayed.
|
||||
*/
|
||||
void clearViewer() {
|
||||
synchronized (this) {
|
||||
thumbnailListModel.removeAllElements();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the thumbnail for an image to the panel.
|
||||
*
|
||||
* @param thumbnailWrapper The object which contains the thumbnail which
|
||||
* will be displayed.
|
||||
*/
|
||||
void addImage(ImageThumbnailWrapper thumbnailWrapper) {
|
||||
synchronized (this) {
|
||||
thumbnailListModel.addElement(thumbnailWrapper);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Autopsy
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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 org.sleuthkit.autopsy.filequery;
|
||||
|
||||
import java.awt.Image;
|
||||
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
||||
|
||||
/**
|
||||
* Class to wrap all the information necessary for an image thumbnail to be
|
||||
* displayed.
|
||||
*/
|
||||
public class ImageThumbnailWrapper {
|
||||
|
||||
private Image thumbnail;
|
||||
private final ResultFile resultFile;
|
||||
|
||||
/**
|
||||
* Construct a new ImageThumbnailsWrapper.
|
||||
*
|
||||
* @param file The ResultFile which represents the image file which the
|
||||
* thumbnails were created for.
|
||||
*/
|
||||
ImageThumbnailWrapper(ResultFile file) {
|
||||
this.thumbnail = ImageUtils.getDefaultThumbnail();
|
||||
this.resultFile = file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the image thumbnail which exists.
|
||||
*
|
||||
* @param thumbnail The thumbnail which exists for this file.
|
||||
*/
|
||||
void setImageThumbnail(Image thumbnail) {
|
||||
this.thumbnail = thumbnail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ResultFile which represents the image file which the thumbnail
|
||||
* was created for.
|
||||
*
|
||||
* @return The ResultFile which represents the image file which the
|
||||
* thumbnail was created for.
|
||||
*/
|
||||
ResultFile getResultFile() {
|
||||
return resultFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the thumbnail for the image.
|
||||
*
|
||||
* @return The Image which is the thumbnail for the image.
|
||||
*/
|
||||
Image getThumbnail() {
|
||||
return thumbnail;
|
||||
}
|
||||
}
|
@ -238,10 +238,18 @@
|
||||
<Dimension value="[777, 125]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="instancesPanel">
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
</AuxValues>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
|
||||
<JSplitPaneConstraints position="right"/>
|
||||
@ -269,6 +277,8 @@
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="instancesScrollPane">
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
|
||||
@ -286,6 +296,7 @@
|
||||
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="instancesListModel" type="code"/>
|
||||
</Property>
|
||||
<Property name="selectionMode" type="int" value="0"/>
|
||||
<Property name="cellRenderer" type="javax.swing.ListCellRenderer" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="new InstancesCellRenderer()" type="code"/>
|
||||
</Property>
|
||||
|
@ -33,13 +33,12 @@ import javax.swing.SwingUtilities;
|
||||
import javax.swing.SwingWorker;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail;
|
||||
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
|
||||
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
||||
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
@ -51,9 +50,9 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
public class ResultsPanel extends javax.swing.JPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final DataResultViewerThumbnail thumbnailViewer;
|
||||
private final DataResultViewerTable tableViewer;
|
||||
private final VideoThumbnailViewer videoThumbnailViewer;
|
||||
private final ImageThumbnailViewer imageThumbnailViewer;
|
||||
private List<FileSearchFiltering.FileFilter> searchFilters;
|
||||
private FileSearch.AttributeType groupingAttribute;
|
||||
private FileGroup.GroupSortingAlgorithm groupSort;
|
||||
@ -74,22 +73,35 @@ public class ResultsPanel extends javax.swing.JPanel {
|
||||
public ResultsPanel(ExplorerManager explorerManager, EamDb centralRepo) {
|
||||
initComponents();
|
||||
this.centralRepo = centralRepo;
|
||||
thumbnailViewer = new DataResultViewerThumbnail(explorerManager);
|
||||
tableViewer = new DataResultViewerTable(explorerManager);
|
||||
imageThumbnailViewer = new ImageThumbnailViewer();
|
||||
videoThumbnailViewer = new VideoThumbnailViewer();
|
||||
// Disable manual editing of page size spinner
|
||||
videoThumbnailViewer.addListSelectionListener((e) -> {
|
||||
if (!e.getValueIsAdjusting()) {
|
||||
populateInstancesList();
|
||||
}
|
||||
});
|
||||
imageThumbnailViewer.addListSelectionListener((e) -> {
|
||||
if (!e.getValueIsAdjusting()) {
|
||||
populateInstancesList();
|
||||
}
|
||||
});
|
||||
// Disable manual editing of page size spinner
|
||||
((JSpinner.DefaultEditor) pageSizeSpinner.getEditor()).getTextField().setEditable(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a list selection listener to the instances list.
|
||||
*
|
||||
* @param listener The ListSelectionListener to add to the instances list.
|
||||
*/
|
||||
void addListSelectionListener(ListSelectionListener listener) {
|
||||
instancesList.addListSelectionListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the instances list.
|
||||
*/
|
||||
synchronized void populateInstancesList() {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
instancesListModel.removeAllElements();
|
||||
@ -102,6 +114,12 @@ public class ResultsPanel extends javax.swing.JPanel {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the AbstractFile for the item currently selected in the instances
|
||||
* list.
|
||||
*
|
||||
* @return The AbstractFile which is currently selected.
|
||||
*/
|
||||
synchronized AbstractFile getSelectedFile() {
|
||||
if (instancesList.getSelectedIndex() == -1) {
|
||||
return null;
|
||||
@ -110,9 +128,18 @@ public class ResultsPanel extends javax.swing.JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of all instances for the the currently selected item in the
|
||||
* results viewer area.
|
||||
*
|
||||
* @return The list of AbstractFiles which are represented by the item
|
||||
* selected in the results viewer area.
|
||||
*/
|
||||
private List<AbstractFile> getInstancesForSelected() {
|
||||
if (resultType == FileSearchData.FileType.VIDEO) {
|
||||
return videoThumbnailViewer.getInstancesForSelected();
|
||||
} else if (resultType == FileSearchData.FileType.IMAGE) {
|
||||
return imageThumbnailViewer.getInstancesForSelected();
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
@ -128,23 +155,13 @@ public class ResultsPanel extends javax.swing.JPanel {
|
||||
populateInstancesList();
|
||||
currentPage = pageRetrievedEvent.getPageNumber();
|
||||
updateControls();
|
||||
thumbnailViewer.resetComponent();
|
||||
tableViewer.resetComponent();
|
||||
resultsViewerPanel.remove(thumbnailViewer);
|
||||
resultsViewerPanel.remove(tableViewer);
|
||||
resultsViewerPanel.remove(videoThumbnailViewer);
|
||||
resetResultViewer();
|
||||
if (pageRetrievedEvent.getType() == FileSearchData.FileType.IMAGE) {
|
||||
resultsViewerPanel.add(thumbnailViewer);
|
||||
if (pageRetrievedEvent.getSearchResults().size() > 0) {
|
||||
List<AbstractFile> filesList = pageRetrievedEvent.getSearchResults().stream().map(file -> file.getFirstInstance()).collect(Collectors.toList());
|
||||
thumbnailViewer.setNode(new TableFilterNode(new DataResultFilterNode(new AbstractNode(new DiscoveryThumbnailChildren(filesList))), true));
|
||||
} else {
|
||||
thumbnailViewer.setNode(new TableFilterNode(new DataResultFilterNode(Node.EMPTY), true));
|
||||
}
|
||||
populateImageViewer(pageRetrievedEvent.getSearchResults());
|
||||
resultsViewerPanel.add(imageThumbnailViewer);
|
||||
} else if (pageRetrievedEvent.getType() == FileSearchData.FileType.VIDEO) {
|
||||
populateVideoViewer(pageRetrievedEvent.getSearchResults());
|
||||
resultsViewerPanel.add(videoThumbnailViewer);
|
||||
|
||||
} else {
|
||||
resultsViewerPanel.add(tableViewer);
|
||||
if (pageRetrievedEvent.getSearchResults().size() > 0) {
|
||||
@ -154,10 +171,22 @@ public class ResultsPanel extends javax.swing.JPanel {
|
||||
tableViewer.setNode(new TableFilterNode(new DataResultFilterNode(Node.EMPTY), true));
|
||||
}
|
||||
}
|
||||
});
|
||||
resultsViewerPanel.revalidate();
|
||||
resultsViewerPanel.repaint();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
synchronized void populateVideoViewer(List<ResultFile> files) {
|
||||
/**
|
||||
* Reset the result viewer and any associate workers to a default empty
|
||||
* state.
|
||||
*/
|
||||
private synchronized void resetResultViewer() {
|
||||
resultsViewerPanel.remove(imageThumbnailViewer);
|
||||
tableViewer.resetComponent();
|
||||
resultsViewerPanel.remove(tableViewer);
|
||||
resultsViewerPanel.remove(videoThumbnailViewer);
|
||||
|
||||
//cancel any unfished thumb workers
|
||||
for (SwingWorker<Void, Void> thumbWorker : thumbnailWorkers) {
|
||||
if (!thumbWorker.isDone()) {
|
||||
@ -167,6 +196,16 @@ public class ResultsPanel extends javax.swing.JPanel {
|
||||
//clear old thumbnails
|
||||
thumbnailWorkers.clear();
|
||||
videoThumbnailViewer.clearViewer();
|
||||
imageThumbnailViewer.clearViewer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the video thumbnail viewer, cancelling any thumbnails which are
|
||||
* currently being created first.
|
||||
*
|
||||
* @param files The list of ResultFiles to populate the video viewer with.
|
||||
*/
|
||||
synchronized void populateVideoViewer(List<ResultFile> files) {
|
||||
for (ResultFile file : files) {
|
||||
VideoThumbnailWorker thumbWorker = new VideoThumbnailWorker(file);
|
||||
thumbWorker.execute();
|
||||
@ -175,6 +214,21 @@ public class ResultsPanel extends javax.swing.JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the image thumbnail viewer, cancelling any thumbnails which are
|
||||
* currently being created first.
|
||||
*
|
||||
* @param files The list of ResultFiles to populate the image viewer with.
|
||||
*/
|
||||
synchronized void populateImageViewer(List<ResultFile> files) {
|
||||
for (ResultFile file : files) {
|
||||
ImageThumbnailWorker thumbWorker = new ImageThumbnailWorker(file);
|
||||
thumbWorker.execute();
|
||||
//keep track of thumb worker for possible cancelation
|
||||
thumbnailWorkers.add(thumbWorker);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe and respond to GroupSelectedEvents.
|
||||
*
|
||||
@ -201,8 +255,7 @@ public class ResultsPanel extends javax.swing.JPanel {
|
||||
currentPage = 0;
|
||||
updateControls();
|
||||
videoThumbnailViewer.clearViewer();
|
||||
thumbnailViewer.resetComponent();
|
||||
thumbnailViewer.setNode(new TableFilterNode(new DataResultFilterNode(Node.EMPTY), true));
|
||||
imageThumbnailViewer.clearViewer();
|
||||
tableViewer.setNode(new TableFilterNode(new DataResultFilterNode(Node.EMPTY), true));
|
||||
resultsViewerPanel.revalidate();
|
||||
resultsViewerPanel.repaint();
|
||||
@ -262,9 +315,9 @@ public class ResultsPanel extends javax.swing.JPanel {
|
||||
javax.swing.JLabel gotoPageLabel = new javax.swing.JLabel();
|
||||
gotoPageField = new javax.swing.JTextField();
|
||||
javax.swing.JLabel pageSizeLabel = new javax.swing.JLabel();
|
||||
resultsSplitPane = new javax.swing.JSplitPane();
|
||||
instancesPanel = new javax.swing.JPanel();
|
||||
instancesScrollPane = new javax.swing.JScrollPane();
|
||||
javax.swing.JSplitPane resultsSplitPane = new javax.swing.JSplitPane();
|
||||
javax.swing.JPanel instancesPanel = new javax.swing.JPanel();
|
||||
javax.swing.JScrollPane instancesScrollPane = new javax.swing.JScrollPane();
|
||||
instancesList = new javax.swing.JList<>();
|
||||
resultsViewerPanel = new javax.swing.JPanel();
|
||||
|
||||
@ -374,6 +427,7 @@ public class ResultsPanel extends javax.swing.JPanel {
|
||||
|
||||
instancesList.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(ResultsPanel.class, "ResultsPanel.instancesList.border.title"))); // NOI18N
|
||||
instancesList.setModel(instancesListModel);
|
||||
instancesList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
|
||||
instancesList.setCellRenderer(new InstancesCellRenderer());
|
||||
instancesScrollPane.setViewportView(instancesList);
|
||||
|
||||
@ -489,33 +543,82 @@ public class ResultsPanel extends javax.swing.JPanel {
|
||||
private javax.swing.JLabel currentPageLabel;
|
||||
private javax.swing.JTextField gotoPageField;
|
||||
private javax.swing.JList<AbstractFile> instancesList;
|
||||
private javax.swing.JPanel instancesPanel;
|
||||
private javax.swing.JScrollPane instancesScrollPane;
|
||||
private javax.swing.JButton nextPageButton;
|
||||
private javax.swing.JSpinner pageSizeSpinner;
|
||||
private javax.swing.JPanel pagingPanel;
|
||||
private javax.swing.JButton previousPageButton;
|
||||
private javax.swing.JSplitPane resultsSplitPane;
|
||||
private javax.swing.JPanel resultsViewerPanel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
/**
|
||||
* Swing worker to handle the retrieval of video thumbnails and population
|
||||
* of the Video Thumbnail Viewer.
|
||||
*/
|
||||
private class VideoThumbnailWorker extends SwingWorker<Void, Void> {
|
||||
|
||||
private final VideoThumbnailsWrapper thumbnailWrapper;
|
||||
|
||||
/**
|
||||
* Construct a new VideoThumbnailWorker.
|
||||
*
|
||||
* @param file The ResultFile which represents the video file thumbnails
|
||||
* are being retrieved for.
|
||||
*/
|
||||
VideoThumbnailWorker(ResultFile file) {
|
||||
thumbnailWrapper = new VideoThumbnailsWrapper(new ArrayList<Image>(), new int[4], file);
|
||||
videoThumbnailViewer.addRow(thumbnailWrapper);
|
||||
thumbnailWrapper = new VideoThumbnailsWrapper(file);
|
||||
videoThumbnailViewer.addVideo(thumbnailWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
FileSearch.getVideoThumbnails(thumbnailWrapper);
|
||||
videoThumbnailViewer.repaint();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
videoThumbnailViewer.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Swing worker to handle the retrieval of image thumbnails and population
|
||||
* of the Image Thumbnail Viewer.
|
||||
*/
|
||||
private class ImageThumbnailWorker extends SwingWorker<Void, Void> {
|
||||
|
||||
private final ImageThumbnailWrapper thumbnailWrapper;
|
||||
|
||||
/**
|
||||
* Construct a new ImageThumbnailWorker.
|
||||
*
|
||||
* @param file The ResultFile which represents the image file thumbnails
|
||||
* are being retrieved for.
|
||||
*/
|
||||
ImageThumbnailWorker(ResultFile file) {
|
||||
thumbnailWrapper = new ImageThumbnailWrapper(file);
|
||||
imageThumbnailViewer.addImage(thumbnailWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
Image thumbnail = ImageUtils.getThumbnail(thumbnailWrapper.getResultFile().getFirstInstance(), ImageUtils.ICON_SIZE_LARGE);
|
||||
if (thumbnail != null) {
|
||||
thumbnailWrapper.setImageThumbnail(thumbnail);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
imageThumbnailViewer.repaint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Cell renderer for the instances list.
|
||||
*/
|
||||
private class InstancesCellRenderer extends DefaultListCellRenderer {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
@ -150,8 +150,8 @@ class SearchResults {
|
||||
*
|
||||
* @return the grouped and sorted results
|
||||
*/
|
||||
LinkedHashMap<String, List<ResultFile>> toLinkedHashMap() throws FileSearchException {
|
||||
LinkedHashMap<String, List<ResultFile>> map = new LinkedHashMap<>();
|
||||
Map<String, List<ResultFile>> toLinkedHashMap() throws FileSearchException {
|
||||
Map<String, List<ResultFile>> map = new LinkedHashMap<>();
|
||||
|
||||
// Sort the groups and files
|
||||
sortGroupsAndFiles();
|
||||
|
@ -47,7 +47,7 @@
|
||||
<Component id="fileSizeLabel" pref="19" max="32767" attributes="0"/>
|
||||
<Component id="countLabel" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
|
@ -35,7 +35,7 @@ import org.openide.util.NbBundle.Messages;
|
||||
final class VideoThumbnailPanel extends javax.swing.JPanel implements ListCellRenderer<VideoThumbnailsWrapper> {
|
||||
|
||||
private static final int GAP_SIZE = 4;
|
||||
private static final Color SELECTION_COLOR = new Color(100, 200, 255);
|
||||
private static final Color SELECTION_COLOR = new Color(0,120,215);
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
@ -124,7 +124,7 @@ final class VideoThumbnailPanel extends javax.swing.JPanel implements ListCellRe
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
|
||||
.addComponent(fileSizeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 19, Short.MAX_VALUE)
|
||||
.addComponent(countLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addContainerGap())
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
|
@ -50,21 +50,12 @@ public class VideoThumbnailViewer extends javax.swing.JPanel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the AbstractFile associated with the selected thumbnails.
|
||||
* Get the list of AbstractFiles which are represented by the selected Video
|
||||
* thumbnails.
|
||||
*
|
||||
* @return The AbstractFile associated with the selected thumbnails, or null
|
||||
* if no thumbnails are selected.
|
||||
* @return The list of AbstractFiles which are represented by the selected Video
|
||||
* thumbnails.
|
||||
*/
|
||||
AbstractFile getSelectedFile() {
|
||||
synchronized (this) {
|
||||
if (thumbnailList.getSelectedIndex() == -1) {
|
||||
return null;
|
||||
} else {
|
||||
return thumbnailListModel.getElementAt(thumbnailList.getSelectedIndex()).getResultFile().getFirstInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<AbstractFile> getInstancesForSelected() {
|
||||
synchronized (this) {
|
||||
if (thumbnailList.getSelectedIndex() == -1) {
|
||||
@ -90,7 +81,7 @@ public class VideoThumbnailViewer extends javax.swing.JPanel {
|
||||
* @param thumbnailWrapper The object which contains the thumbnails which
|
||||
* will be displayed.
|
||||
*/
|
||||
void addRow(VideoThumbnailsWrapper thumbnailWrapper) {
|
||||
void addVideo(VideoThumbnailsWrapper thumbnailWrapper) {
|
||||
synchronized (this) {
|
||||
thumbnailListModel.addElement(thumbnailWrapper);
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
package org.sleuthkit.autopsy.filequery;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@ -28,23 +29,19 @@ import java.util.List;
|
||||
*/
|
||||
final class VideoThumbnailsWrapper {
|
||||
|
||||
private List<Image> thumbnails;
|
||||
private final List<Image> thumbnails;
|
||||
private final ResultFile resultFile;
|
||||
private int[] timeStamps;
|
||||
|
||||
/**
|
||||
* Construct a new VideoThumbnailsWrapper.
|
||||
*
|
||||
* @param thumbnails The list of Images which are the thumbnails for the
|
||||
* video.
|
||||
* @param timeStamps An array containing the time in milliseconds into the
|
||||
* video that each thumbnail created for.
|
||||
* @param file The ResultFile which represents the video file which
|
||||
* the thumbnails were created for.
|
||||
* @param file The ResultFile which represents the video file which the
|
||||
* thumbnails were created for.
|
||||
*/
|
||||
VideoThumbnailsWrapper(List<Image> thumbnails, int[] timeStamps, ResultFile file) {
|
||||
this.thumbnails = thumbnails;
|
||||
this.timeStamps = timeStamps;
|
||||
VideoThumbnailsWrapper(ResultFile file) {
|
||||
this.thumbnails = new ArrayList<>();
|
||||
this.timeStamps = new int[4];
|
||||
this.resultFile = file;
|
||||
}
|
||||
|
||||
@ -79,9 +76,17 @@ final class VideoThumbnailsWrapper {
|
||||
return Collections.unmodifiableList(thumbnails);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the thumbnails and their associated time stamps.
|
||||
*
|
||||
* @param videoThumbnails The list of Images which are the thumbnails for
|
||||
* the video.
|
||||
* @param framePositions An array containing the time in milliseconds into
|
||||
* the video that each thumbnail created for.
|
||||
*/
|
||||
void setThumbnails(List<Image> videoThumbnails, int[] framePositions) {
|
||||
this.thumbnails = videoThumbnails;
|
||||
this.timeStamps = framePositions;
|
||||
this.thumbnails.addAll(videoThumbnails);
|
||||
this.timeStamps = framePositions.clone();
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user