mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 13:17:41 +00:00
GUACAMOLE-190: Merge client thumbnail tab favicon.
This commit is contained in:
@@ -35,6 +35,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
||||
var clipboardService = $injector.get('clipboardService');
|
||||
var guacClientManager = $injector.get('guacClientManager');
|
||||
var guacNotification = $injector.get('guacNotification');
|
||||
var iconService = $injector.get('iconService');
|
||||
var preferenceService = $injector.get('preferenceService');
|
||||
var tunnelService = $injector.get('tunnelService');
|
||||
var userPageService = $injector.get('userPageService');
|
||||
@@ -403,6 +404,11 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
||||
|
||||
});
|
||||
|
||||
// Update page icon when thumbnail changes
|
||||
$scope.$watch('client.thumbnail.canvas', function thumbnailChanged(canvas) {
|
||||
iconService.setIcons(canvas);
|
||||
});
|
||||
|
||||
// Watch clipboard for new data, associating it with any pressed keys
|
||||
$scope.$watch('client.clipboardData', function clipboardChanged(data) {
|
||||
|
||||
|
@@ -24,14 +24,15 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
||||
function defineManagedClient($rootScope, $injector) {
|
||||
|
||||
// Required types
|
||||
var ClientProperties = $injector.get('ClientProperties');
|
||||
var ClientIdentifier = $injector.get('ClientIdentifier');
|
||||
var ClipboardData = $injector.get('ClipboardData');
|
||||
var ManagedClientState = $injector.get('ManagedClientState');
|
||||
var ManagedDisplay = $injector.get('ManagedDisplay');
|
||||
var ManagedFilesystem = $injector.get('ManagedFilesystem');
|
||||
var ManagedFileUpload = $injector.get('ManagedFileUpload');
|
||||
var ManagedShareLink = $injector.get('ManagedShareLink');
|
||||
var ClientProperties = $injector.get('ClientProperties');
|
||||
var ClientIdentifier = $injector.get('ClientIdentifier');
|
||||
var ClipboardData = $injector.get('ClipboardData');
|
||||
var ManagedClientState = $injector.get('ManagedClientState');
|
||||
var ManagedClientThumbnail = $injector.get('ManagedClientThumbnail');
|
||||
var ManagedDisplay = $injector.get('ManagedDisplay');
|
||||
var ManagedFilesystem = $injector.get('ManagedFilesystem');
|
||||
var ManagedFileUpload = $injector.get('ManagedFileUpload');
|
||||
var ManagedShareLink = $injector.get('ManagedShareLink');
|
||||
|
||||
// Required services
|
||||
var $document = $injector.get('$document');
|
||||
@@ -46,7 +47,15 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
||||
var guacHistory = $injector.get('guacHistory');
|
||||
var guacImage = $injector.get('guacImage');
|
||||
var guacVideo = $injector.get('guacVideo');
|
||||
|
||||
|
||||
/**
|
||||
* The minimum amount of time to wait between updates to the client
|
||||
* thumbnail, in milliseconds.
|
||||
*
|
||||
* @type Number
|
||||
*/
|
||||
var THUMBNAIL_UPDATE_FREQUENCY = 5000;
|
||||
|
||||
/**
|
||||
* Object which serves as a surrogate interface, encapsulating a Guacamole
|
||||
* client while it is active, allowing it to be detached and reattached
|
||||
@@ -98,6 +107,15 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
||||
*/
|
||||
this.name = template.name;
|
||||
|
||||
/**
|
||||
* The most recently-generated thumbnail for this connection, as
|
||||
* stored within the local connection history. If no thumbnail is
|
||||
* stored, this will be null.
|
||||
*
|
||||
* @type ManagedClientThumbnail
|
||||
*/
|
||||
this.thumbnail = template.thumbnail;
|
||||
|
||||
/**
|
||||
* The current clipboard contents.
|
||||
*
|
||||
@@ -227,45 +245,6 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Store the thumbnail of the given managed client within the connection
|
||||
* history under its associated ID. If the client is not connected, this
|
||||
* function has no effect.
|
||||
*
|
||||
* @param {String} managedClient
|
||||
* The client whose history entry should be updated.
|
||||
*/
|
||||
var updateHistoryEntry = function updateHistoryEntry(managedClient) {
|
||||
|
||||
var display = managedClient.client.getDisplay();
|
||||
|
||||
// Update stored thumbnail of previous connection
|
||||
if (display && display.getWidth() > 0 && display.getHeight() > 0) {
|
||||
|
||||
// Get screenshot
|
||||
var canvas = display.flatten();
|
||||
|
||||
// Calculate scale of thumbnail (max 320x240, max zoom 100%)
|
||||
var scale = Math.min(320 / canvas.width, 240 / canvas.height, 1);
|
||||
|
||||
// Create thumbnail canvas
|
||||
var thumbnail = $document[0].createElement("canvas");
|
||||
thumbnail.width = canvas.width*scale;
|
||||
thumbnail.height = canvas.height*scale;
|
||||
|
||||
// Scale screenshot to thumbnail
|
||||
var context = thumbnail.getContext("2d");
|
||||
context.drawImage(canvas,
|
||||
0, 0, canvas.width, canvas.height,
|
||||
0, 0, thumbnail.width, thumbnail.height
|
||||
);
|
||||
|
||||
guacHistory.updateThumbnail(managedClient.id, thumbnail.toDataURL("image/png"));
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Requests the creation of a new audio stream, recorded from the user's
|
||||
* local audio input device. If audio input is supported by the connection,
|
||||
@@ -403,12 +382,14 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
||||
// Begin streaming audio input if possible
|
||||
requestAudioStream(client);
|
||||
|
||||
// Update thumbnail with initial display contents
|
||||
ManagedClient.updateThumbnail(managedClient);
|
||||
break;
|
||||
|
||||
// Update history when disconnecting
|
||||
case 4: // Disconnecting
|
||||
case 5: // Disconnected
|
||||
updateHistoryEntry(managedClient);
|
||||
ManagedClient.updateThumbnail(managedClient);
|
||||
break;
|
||||
|
||||
}
|
||||
@@ -431,6 +412,21 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
||||
});
|
||||
};
|
||||
|
||||
// Automatically update the client thumbnail
|
||||
client.onsync = function syncReceived() {
|
||||
|
||||
var thumbnail = managedClient.thumbnail;
|
||||
var timestamp = new Date().getTime();
|
||||
|
||||
// Update thumbnail if it doesn't exist or is old
|
||||
if (!thumbnail || timestamp - thumbnail.timestamp >= THUMBNAIL_UPDATE_FREQUENCY) {
|
||||
$rootScope.$apply(function updateClientThumbnail() {
|
||||
ManagedClient.updateThumbnail(managedClient);
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Handle any received clipboard data
|
||||
client.onclipboard = function clientClipboardReceived(stream, mimetype) {
|
||||
|
||||
@@ -651,6 +647,52 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Store the thumbnail of the given managed client within the connection
|
||||
* history under its associated ID. If the client is not connected, this
|
||||
* function has no effect.
|
||||
*
|
||||
* @param {ManagedClient} managedClient
|
||||
* The client whose history entry should be updated.
|
||||
*/
|
||||
ManagedClient.updateThumbnail = function updateThumbnail(managedClient) {
|
||||
|
||||
var display = managedClient.client.getDisplay();
|
||||
|
||||
// Update stored thumbnail of previous connection
|
||||
if (display && display.getWidth() > 0 && display.getHeight() > 0) {
|
||||
|
||||
// Get screenshot
|
||||
var canvas = display.flatten();
|
||||
|
||||
// Calculate scale of thumbnail (max 320x240, max zoom 100%)
|
||||
var scale = Math.min(320 / canvas.width, 240 / canvas.height, 1);
|
||||
|
||||
// Create thumbnail canvas
|
||||
var thumbnail = $document[0].createElement("canvas");
|
||||
thumbnail.width = canvas.width*scale;
|
||||
thumbnail.height = canvas.height*scale;
|
||||
|
||||
// Scale screenshot to thumbnail
|
||||
var context = thumbnail.getContext("2d");
|
||||
context.drawImage(canvas,
|
||||
0, 0, canvas.width, canvas.height,
|
||||
0, 0, thumbnail.width, thumbnail.height
|
||||
);
|
||||
|
||||
// Store updated thumbnail within client
|
||||
managedClient.thumbnail = new ManagedClientThumbnail({
|
||||
timestamp : new Date().getTime(),
|
||||
canvas : thumbnail
|
||||
});
|
||||
|
||||
// Update historical thumbnail
|
||||
guacHistory.updateThumbnail(managedClient.id, thumbnail.toDataURL("image/png"));
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return ManagedClient;
|
||||
|
||||
}]);
|
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides the ManagedClientThumbnail class used by ManagedClient.
|
||||
*/
|
||||
angular.module('client').factory('ManagedClientThumbnail', [function defineManagedClientThumbnail() {
|
||||
|
||||
/**
|
||||
* Object which represents a thumbnail of the Guacamole client display,
|
||||
* along with the time that the thumbnail was generated.
|
||||
*
|
||||
* @constructor
|
||||
* @param {ManagedClientThumbnail|Object} [template={}]
|
||||
* The object whose properties should be copied within the new
|
||||
* ManagedClientThumbnail.
|
||||
*/
|
||||
var ManagedClientThumbnail = function ManagedClientThumbnail(template) {
|
||||
|
||||
// Use empty object by default
|
||||
template = template || {};
|
||||
|
||||
/**
|
||||
* The time that this thumbnail was generated, as the number of
|
||||
* milliseconds elapsed since midnight of January 1, 1970 UTC.
|
||||
*
|
||||
* @type Number
|
||||
*/
|
||||
this.timestamp = template.timestamp;
|
||||
|
||||
/**
|
||||
* The thumbnail of the Guacamole client display.
|
||||
*
|
||||
* @type HTMLCanvasElement
|
||||
*/
|
||||
this.canvas = template.canvas;
|
||||
|
||||
};
|
||||
|
||||
return ManagedClientThumbnail;
|
||||
|
||||
}]);
|
148
guacamole/src/main/webapp/app/index/services/iconService.js
Normal file
148
guacamole/src/main/webapp/app/index/services/iconService.js
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A service for updating or resetting the favicon of the current page.
|
||||
*/
|
||||
angular.module('index').factory('iconService', ['$rootScope', function iconService($rootScope) {
|
||||
|
||||
var service = {};
|
||||
|
||||
/**
|
||||
* The URL of the image used for the low-resolution (64x64) favicon. This
|
||||
* MUST match the URL which is set statically within index.html.
|
||||
*
|
||||
* @constant
|
||||
* @type String
|
||||
*/
|
||||
var DEFAULT_SMALL_ICON_URL = 'images/logo-64.png';
|
||||
|
||||
/**
|
||||
* The URL of the image used for the high-resolution (144x144) favicon. This
|
||||
* MUST match the URL which is set statically within index.html.
|
||||
*
|
||||
* @constant
|
||||
* @type String
|
||||
*/
|
||||
var DEFAULT_LARGE_ICON_URL = 'images/logo-144.png';
|
||||
|
||||
/**
|
||||
* JQuery-wrapped array of all link tags which point to the small,
|
||||
* low-resolution page icon.
|
||||
*
|
||||
* @type Element[]
|
||||
*/
|
||||
var smallIcons = $('link[rel=icon][href="' + DEFAULT_SMALL_ICON_URL + '"]');
|
||||
|
||||
/**
|
||||
* JQuery-wrapped array of all link tags which point to the large,
|
||||
* high-resolution page icon.
|
||||
*
|
||||
* @type Element[]
|
||||
*/
|
||||
var largeIcons = $('link[rel=icon][href="' + DEFAULT_LARGE_ICON_URL + '"]');
|
||||
|
||||
/**
|
||||
* Generates an icon by scaling the provided image to fit the given
|
||||
* dimensions, returning a canvas containing the generated icon.
|
||||
*
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* A canvas element containing the image which should be scaled to
|
||||
* produce the contents of the generated icon.
|
||||
*
|
||||
* @param {Number} width
|
||||
* The width of the icon to generate, in pixels.
|
||||
*
|
||||
* @param {Number} height
|
||||
* The height of the icon to generate, in pixels.
|
||||
*
|
||||
* @returns {HTMLCanvasElement}
|
||||
* A new canvas element having the given dimensions and containing the
|
||||
* provided image, scaled to fit.
|
||||
*/
|
||||
var generateIcon = function generateIcon(canvas, width, height) {
|
||||
|
||||
// Create icon canvas having the provided dimensions
|
||||
var icon = document.createElement('canvas');
|
||||
icon.width = width;
|
||||
icon.height = height;
|
||||
|
||||
// Calculate the scale factor necessary to fit the provided image
|
||||
// within the icon dimensions
|
||||
var scale = Math.min(width / canvas.width, height / canvas.height);
|
||||
|
||||
// Calculate the dimensions and position of the scaled image within
|
||||
// the icon, offsetting the image such that it is centered
|
||||
var scaledWidth = canvas.width * scale;
|
||||
var scaledHeight = canvas.height * scale;
|
||||
var offsetX = (width - scaledWidth) / 2;
|
||||
var offsetY = (height - scaledHeight) / 2;
|
||||
|
||||
// Draw the icon, scaling the provided image as necessary
|
||||
var context = icon.getContext('2d');
|
||||
context.drawImage(canvas, offsetX, offsetY, scaledWidth, scaledHeight);
|
||||
return icon;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Temporarily sets the icon of the current page to the contents of the
|
||||
* given canvas element. The image within the canvas element will be
|
||||
* automatically scaled and centered to fit within the dimensions of the
|
||||
* page icons. The page icons will be automatically reset to their original
|
||||
* values upon navigation.
|
||||
*
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* The canvas element containing the icon. If this value is null or
|
||||
* undefined, this function has no effect.
|
||||
*/
|
||||
service.setIcons = function setIcons(canvas) {
|
||||
|
||||
// Do nothing if no canvas provided
|
||||
if (!canvas)
|
||||
return;
|
||||
|
||||
// Assign low-resolution (64x64) icon
|
||||
var smallIcon = generateIcon(canvas, 64, 64);
|
||||
smallIcons.attr('href', smallIcon.toDataURL('image/png'));
|
||||
|
||||
// Assign high-resolution (144x144) icon
|
||||
var largeIcon = generateIcon(canvas, 144, 144);
|
||||
largeIcons.attr('href', largeIcon.toDataURL('image/png'));
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Resets the icons of the current page to their original values, undoing
|
||||
* any previous calls to setIcons(). This function is automatically invoked
|
||||
* upon navigation.
|
||||
*/
|
||||
service.setDefaultIcons = function setDefaultIcons() {
|
||||
smallIcons.attr('href', DEFAULT_SMALL_ICON_URL);
|
||||
largeIcons.attr('href', DEFAULT_LARGE_ICON_URL);
|
||||
};
|
||||
|
||||
// Automatically reset page icons after navigation
|
||||
$rootScope.$on('$routeChangeSuccess', function resetIcon() {
|
||||
service.setDefaultIcons();
|
||||
});
|
||||
|
||||
return service;
|
||||
|
||||
}]);
|
Reference in New Issue
Block a user