VulpesOS/gaia/apps/ringtones/js/share.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

247 lines
8.3 KiB
JavaScript
Executable file

/* global CustomDialog, NotificationHelper */
'use strict';
screen.mozLockOrientation('portrait');
// This page can be launched in one of two ways: as an activity opened by
// another app, or by clicking the "+" icon in the ringtone manager. First up,
// let's handle the activity case.
if (document.location.hash === '#activity') {
navigator.mozSetMessageHandler('activity', function(activity) {
// Turn an array of data into its first element; we need this to make
// "share" activity data look more like "pick" activity data. We still want
// to preserve the rest of the structure though, so that other attributes
// get passed along.
function flatten(arr) {
return arr && arr.length ? arr[0] : null;
}
var data = activity.source.data;
data.blob = flatten(data.blobs);
data.metadata = flatten(data.metadata);
data.name = flatten(data.filenames);
handleShare(data, function(command, details) {
switch (command) {
case 'save':
activity.postResult({});
break;
case 'cancel':
activity.postError('pick cancelled');
break;
case 'error':
activity.postError('pick error');
break;
}
});
});
}
else {
// XXX: If this window was opened from the ringtones manager and we lose
// visibility, we need to close the window so that subsequent calls to
// window.open actually bring the window to the foreground! This is a
// workaround for the fact that the ringtones app is currently has a "system"
// role, which means that it doesn't participate in the window manager's
// stack. See bug 1030954 for more details.
window.addEventListener('visibilitychange', function() {
if (document.hidden) {
window.close();
}
});
window.addEventListener('message', function(event) {
if (event.origin !== window.location.origin) {
console.error('Couldn\'t recieve message: origins don\'t match',
event.origin, window.location.origin);
return;
}
handleShare(event.data, function(command, details) {
event.source.postMessage(
{command: command, details: details}, event.origin
);
window.close();
});
});
}
function handleShare(data, callback) {
navigator.mozL10n.once(function() {
function showError(title, message, okCallback) {
var okButton = {
title: 'ok',
callback: function() {
CustomDialog.hide();
okCallback();
}
};
CustomDialog.show(title, message, okButton);
}
var save = document.getElementById('save');
var header = document.getElementById('header');
var control = document.getElementById('playpause');
var preview = document.getElementById('preview');
var songtitle;
if (data.metadata && data.metadata.title) {
songtitle = data.metadata.title;
}
else if (data.name) {
songtitle = data.name;
}
else {
songtitle = 'ringtone-untitled';
}
var subtitle = '';
if (data.metadata && data.metadata.artist) {
subtitle = data.metadata.artist;
}
document.getElementById('songtitle').textContent = songtitle;
document.getElementById('artist').textContent = subtitle;
if (data.metadata && data.metadata.picture) {
// If we have a picture, make sure the subtitle takes up some vertical
// space so that everything lines up correctly.
if (!subtitle) {
document.getElementById('artist').textContent = '\u00a0'; //  
}
try {
var pictureURL = URL.createObjectURL(data.metadata.picture);
var picture = document.getElementById('picture');
picture.hidden = false;
picture.style.backgroundImage = 'url(' + pictureURL + ')';
}
catch(e) {
console.error('Couldn\'t set picture:', e);
}
}
control.addEventListener('click', function() {
if (preview.paused) {
// HACK HACK HACK
//
// This is an ugly workaround for bug 956811.
//
// Bugs in the system app window manager and Gecko's audio
// channel manager prevent the music app from being paused when
// this set ringtone app previews a ringtone. As a workaround,
// the music app listens to the settings database and will pause
// itself if we set a magic property that it specifies. So if
// the activity data includes a special magic property then we
// know that we were invoked by the music app, and the music app
// is playing a song. The value of the property in the activity
// data is the name of the property in the settings database
// that it is listening to.
//
// See also the corresponding code in apps/music/js/Player.js
//
// This hack is implemented as a single self-invoking function so it is
// easy to remove when we have a proper bug fix.
//
// HACK HACK HACK
(function() {
var hack_activity_property = '_hack_hack_shut_up';
var hack_settings_property = data[hack_activity_property];
if (hack_settings_property) {
// Query the value of this setting
var lock = navigator.mozSettings.createLock();
lock.get(hack_settings_property).onsuccess = function(e) {
var value = e.target.result[hack_settings_property];
// Once we have the value, set the setting to something different.
// This should pause the music app. Wait until we have
// confirmation that the setting was set before starting to play
// the preview so that the music app has time to pause first
var o = {};
o[hack_settings_property] = !value;
navigator.mozSettings.createLock().set(o).onsuccess = function() {
preview.play();
};
// Alter the activity data so we only run this code once.
delete data[hack_activity_property];
};
}
else {
preview.play();
}
}());
// END OF HACK
// When there is a real bug fix, just replace this entire if clause
// with:
// preview.play();
}
else {
preview.pause();
}
});
preview.src = URL.createObjectURL(data.blob);
preview.addEventListener('canplay', function() {
save.disabled = false;
});
preview.addEventListener('error', function() {
showError('play-error-title', 'play-error-desc',
callback.bind(null, 'error'));
});
preview.addEventListener('playing', function() {
control.classList.add('playing');
});
preview.addEventListener('pause', function() {
control.classList.remove('playing');
});
header.addEventListener('action', function() {
callback('cancel');
});
save.addEventListener('click', function() {
// Disable the button so the user can't click it twice.
save.disabled = true;
// For a large blob this can take a while, so display a spinner.
document.getElementById('saving-overlay').hidden = false;
// Add to the custom ringtones DB and then set it in the settings.
var info = {name: songtitle, subtitle: subtitle, blob: data.blob};
window.customRingtones.add(info).then(function(tone) {
if (document.getElementById('default-switch').checked) {
return window.systemTones.set('ringtone', tone).then(function() {
return {toneID: tone.id, setAsDefault: true};
});
}
return {toneID: tone.id, setAsDefault: false};
}).then(function(details) {
// Show a notification indicating success, and then close it immediately
// so it doesn't stink up the notifications tray! XXX: This UX isn't
// great; we should turn this into a transient notification when we can.
NotificationHelper.send(
songtitle,
{ bodyL10n: details.setAsDefault ? 'set-default-tone' :
'created-tone',
closeOnClick: false
}
).then((notification) => {
notification.close();
callback('save', details);
});
}, function(error) {
console.log('Error saving ringtone', error);
showError('save-error-title', 'save-error-desc',
callback.bind(null, 'error'));
});
});
});
}