Author: titmuss
Date: Tue Oct 21 01:40:17 2008
New Revision: 3167

URL: http://svn.slimdevices.com?rev=3167&root=Jive&view=rev
Log:
Bug: 9437
Description:
Add an advanced option to enable beta audio playback for the 7.3 release. Once 
this option is enabled the controller/squeezeplay 
will appear as an option in the Choose Player menu. The option is sticky 
through reboots/restarts.


Added:
    7.3/trunk/squeezeplay/src/squeezeplay/share/applets/Playback/
    
7.3/trunk/squeezeplay/src/squeezeplay/share/applets/Playback/PlaybackApplet.lua
    
7.3/trunk/squeezeplay/src/squeezeplay/share/applets/Playback/PlaybackMeta.lua
    7.3/trunk/squeezeplay/src/squeezeplay/share/applets/Playback/strings.txt   
(with props)
Removed:
    7.3/trunk/squeezeplay/src/squeezeplay/share/applets/TestPlayback/
    7.3/trunk/squeezeplay/src/squeezeplay_desktop/share/applets/TestDecode/
Modified:
    
7.3/trunk/squeezeplay/src/squeezeplay/share/applets/SlimDiscovery/SlimDiscoveryMeta.lua
    7.3/trunk/squeezeplay/src/squeezeplay/share/jive/AppletManager.lua
    7.3/trunk/squeezeplay/src/squeezeplay/share/jive/audio/Playback.lua
    7.3/trunk/squeezeplay/src/squeezeplay/share/jive/net/SlimProto.lua
    7.3/trunk/squeezeplay/src/squeezeplay/share/jive/slim/SlimServer.lua
    7.3/trunk/squeezeplay/src/squeezeplay/src/audio/decode/decode.c
    7.3/trunk/squeezeplay/src/squeezeplay_desktop/Makefile.am
    7.3/trunk/squeezeplay/src/squeezeplay_desktop/Makefile.in

Added: 
7.3/trunk/squeezeplay/src/squeezeplay/share/applets/Playback/PlaybackApplet.lua
URL: 
http://svn.slimdevices.com/7.3/trunk/squeezeplay/src/squeezeplay/share/applets/Playback/PlaybackApplet.lua?rev=3167&root=Jive&view=auto
==============================================================================
--- 
7.3/trunk/squeezeplay/src/squeezeplay/share/applets/Playback/PlaybackApplet.lua 
(added)
+++ 
7.3/trunk/squeezeplay/src/squeezeplay/share/applets/Playback/PlaybackApplet.lua 
Tue Oct 21 01:40:17 2008
@@ -1,0 +1,226 @@
+
+
+-- stuff we use
+local pairs, setmetatable, tostring, tonumber  = pairs, setmetatable, 
tostring, tonumber
+
+local oo            = require("loop.simple")
+local string        = require("string")
+local table         = require("jive.utils.table")
+
+local Applet        = require("jive.Applet")
+local SlimServer    = require("jive.slim.SlimServer")
+local LocalPlayer   = require("jive.slim.LocalPlayer")
+
+local Decode        = require("squeezeplay.decode")
+
+local Framework     = require("jive.ui.Framework")
+local Checkbox      = require("jive.ui.Checkbox")
+local Label         = require("jive.ui.Label")
+local SimpleMenu    = require("jive.ui.SimpleMenu")
+local Window        = require("jive.ui.Window")
+local Textarea      = require("jive.ui.Textarea")
+local Textinput     = require("jive.ui.Textinput")
+local Popup         = require("jive.ui.Popup")
+local Icon          = require("jive.ui.Icon")
+
+local SlimProto     = require("jive.net.SlimProto")
+local Playback      = require("jive.audio.Playback")
+
+local debug         = require("jive.utils.debug")
+local log           = require("jive.utils.log").logger("applets.setup")
+
+local jnt           = jnt
+local jiveMain      = jiveMain
+local appletManager = appletManager
+
+
+module(..., Framework.constants)
+oo.class(_M, Applet)
+
+
+-- main setting menu
+function settingsShow(self, metaState)
+       local settings = self:getSettings()
+
+       local window = Window("window", self:string("AUDIO_PLAYBACK"))
+       local menu = SimpleMenu("menu", items)
+       menu:setComparator(SimpleMenu.itemComparatorWeightAlpha)
+       window:addWidget(menu)
+
+       menu:addItem({
+               text = self:string("ENABLE_AUDIO"),
+               sound = "SELECT",
+               weight = 1,
+               icon = Checkbox(
+                       "checkbox",
+                       function(_, isSelected)
+                               local settings = self:getSettings()
+                               settings.enableAudio = isSelected and 3 or 2
+
+                               if isSelected then
+                                       -- Create player instance
+                                       local uuid, mac = jnt:getUUID()
+                                       metaState.player = LocalPlayer(jnt, 
mac, uuid)
+
+                               else
+                                       -- Disconnect and free player
+                                       local currentPlayer = 
appletManager:callService("getCurrentPlayer")
+                                       if currentPlayer == metaState.player 
then
+                                               
appletManager:callService("setCurrentPlayer", null)
+                                       end
+
+
+                                       
metaState.player:free(metaState.player:getSlimServer())
+                                       metaState.player = nil
+
+                                       settings.serverName = nil
+                                       settings.serverInit = nil
+                                       settings.playerName = nil
+                               end
+
+                               self:storeSettings()
+                       end,
+                        settings.enableAudio & 1 == 1
+               )})
+
+       menu:addItem({
+               text = self:string("DEBUG_AUDIO"),
+               sound = "WINDOWSHOW",
+               weight = 2,
+               callback = function(event, menuItem)
+                                  self:_debugMenu()
+                          end,
+       })
+
+       menu:addItem({
+               text = self:string("TEST_TONES"),
+               sound = "WINDOWSHOW",
+               weight = 2,
+               callback = function(event, menuItem)
+                                  self:_tonesMenu()
+                          end,
+       })
+
+       self:tieAndShowWindow(window)
+end
+
+
+local decoders = {
+      ['p'] = 'wav',
+      ['f'] = 'flac',
+      ['w'] = 'wma',
+      ['9'] = 'ogg',
+      ['m'] = 'mp3',
+      ['t'] = 'tone',
+}
+
+function _debugMenu(self)
+       local window = Window("window", self:string("DEBUG_AUDIO"))
+
+       local values = {}
+       for i=1,6 do
+                       values[i] = Label("value", "")
+       end
+
+       local menu = SimpleMenu("menu", {
+               { text = self:string("FORMAT"), icon = values[1] },
+               { text = self:string("DECODE_BUFFER"), icon = values[2] },
+               { text = self:string("OUTPUT_BUFFER"), icon = values[3] },
+               { text = self:string("ELAPSED_SECS"), icon = values[4] },
+               { text = self:string("NUM_TRACKS"), icon = values[5] },
+               { text = self:string("DECODE_STATE"), icon = values[6] },
+       })
+
+       window:addWidget(menu)
+
+       window:addTimer(1000, function()
+                       local status = Decode:status()
+
+                       values[1]:setValue(decoders[string.char(status.decoder 
or "")] or "?")
+                       values[2]:setValue(string.format('%0.1f%%', 
status.decodeFull / status.decodeSize * 100))
+                       values[3]:setValue(string.format('%0.1f%%', 
status.outputFull / status.outputSize * 100))
+                       values[4]:setValue(status.elapsed)
+                       values[5]:setValue(status.tracksStarted)
+                       values[6]:setValue(status.decodeState .. " " .. 
status.audioState)
+       end)
+
+       window:show()
+       return window
+end
+
+
+function _tonesMenu(self)
+       local window = Window("window", self:string("TEST_TONES"))
+
+       local menu = SimpleMenu("menu", {
+               { text = self:string("MULTITONE"),
+                 sound = "WINDOWSHOW",
+                 callback = function(event)
+                       Decode:start(
+                               string.byte('t'), 0, 0, 0, 0, 0, 1
+                       )
+                       Decode:resume()
+                 end
+               },
+               { text = self:string("SINE_44.1k"),
+                 sound = "WINDOWSHOW",
+                 callback = function(event)
+                       Decode:start(
+                               string.byte('t'), 0, 0, 0, 0, 0, 10
+                       )
+                       Decode:resume()
+                 end
+               },
+               { text = self:string("SINE_48k"),
+                 sound = "WINDOWSHOW",
+                 callback = function(event)
+                       Decode:start(
+                               string.byte('t'), 0, 0, 0, 0, 0, 11
+                       )
+                       Decode:resume()
+                 end
+               },
+               { text = self:string("SINE_88.2K"),
+                 sound = "WINDOWSHOW",
+                 callback = function(event)
+                       Decode:start(
+                               string.byte('t'), 0, 0, 0, 0, 0, 12
+                       )
+                       Decode:resume()
+                 end
+               },
+               { text = self:string("SINE_96K"),
+                 sound = "WINDOWSHOW",
+                 callback = function(event)
+                       Decode:start(
+                               string.byte('t'), 0, 0, 0, 0, 0, 13
+                       )
+                       Decode:resume()
+                 end
+               },                      
+               { text = self:string("SINE_STOP"),
+                 sound = "WINDOWSHOW",
+                 callback = function(event)
+                       Decode:stop()
+                 end
+               },                      
+       })
+
+       window:addWidget(menu)
+
+       window:show()
+       return window
+end
+
+
+--[[
+
+=head1 LICENSE
+
+Copyright 2007 Logitech. All Rights Reserved.
+
+This file is subject to the Logitech Public Source License Version 1.0. Please 
see the LICENCE file for details.
+
+=cut
+--]]
+

Added: 
7.3/trunk/squeezeplay/src/squeezeplay/share/applets/Playback/PlaybackMeta.lua
URL: 
http://svn.slimdevices.com/7.3/trunk/squeezeplay/src/squeezeplay/share/applets/Playback/PlaybackMeta.lua?rev=3167&root=Jive&view=auto
==============================================================================
--- 
7.3/trunk/squeezeplay/src/squeezeplay/share/applets/Playback/PlaybackMeta.lua 
(added)
+++ 
7.3/trunk/squeezeplay/src/squeezeplay/share/applets/Playback/PlaybackMeta.lua 
Tue Oct 21 01:40:17 2008
@@ -1,0 +1,126 @@
+
+local getmetatable = getmetatable
+
+local oo            = require("loop.simple")
+
+local AppletMeta    = require("jive.AppletMeta")
+
+local LocalPlayer   = require("jive.slim.LocalPlayer")
+local SlimServer    = require("jive.slim.SlimServer")
+
+local debug          = require("jive.utils.debug")
+local log            = require("jive.utils.log").logger("applet")
+
+local appletManager = appletManager
+local jiveMain      = jiveMain
+local jnt           = jnt
+
+
+module(...)
+oo.class(_M, AppletMeta)
+
+
+function jiveVersion(self)
+       return 1, 1
+end
+
+
+function defaultSettings(self)
+       return {
+               -- Enable audio playback
+               -- 0 == default off
+               -- 1 == default on
+               -- 2 == user off
+               -- 3 == user on
+               enableAudio = 0,
+       }
+end
+
+
+function registerApplet(meta)
+       -- this allows us to share state with the applet
+       meta.state = {}
+
+       local settings = meta:getSettings()
+
+       if settings.enableAudio & 1 == 1 then
+               -- Create player instance
+               local uuid, mac = jnt:getUUID()
+               meta.state.player = LocalPlayer(jnt, mac, uuid)
+       end
+
+       jiveMain:addItem(meta:menuItem('audioPlayback', 'advancedSettings', 
"AUDIO_PLAYBACK", function(applet, ...) applet:settingsShow(meta.state) end, 1))
+end
+
+
+function configureApplet(meta)
+       if not meta.state.player then
+               -- no audio playback
+               return
+       end
+
+       local settings = meta:getSettings()
+
+       -- Connect player
+       local server = nil
+       if settings.serverName then
+               server = SlimServer(jnt, settings.serverName)
+               server:updateInit(settings.serverInit)
+       end
+
+       meta.state.player:updateInit(server, settings.playerInit)
+
+       -- Subscribe to changes in player status
+       jnt:subscribe(meta)
+end
+
+
+function _updateSettings(meta, player, force)
+       local settings = meta:getSettings()
+
+       local server = player and player:getSlimServer() or false
+       local serverName = server and server:getName() or false
+
+       if not force and
+          settings.serverName == serverName then
+               -- no change
+               return
+       end
+
+       settings.serverName = serverName
+       settings.serverInit = server:getInit()
+       settings.playerInit = player:getInit()
+
+       meta:storeSettings()
+end
+
+
+function notify_playerConnected(meta, player)
+       if meta.state.player ~= player then
+               return
+       end
+
+       _updateSettings(meta, player)
+end
+
+
+function notify_playerNewName(meta, player, playerName)
+       if meta.state.player ~= player then
+               return
+       end
+
+       _updateSettings(meta, player, true)
+end
+
+
+--[[
+
+=head1 LICENSE
+
+Copyright 2007 Logitech. All Rights Reserved.
+
+This file is subject to the Logitech Public Source License Version 1.0. Please 
see the LICENCE file for details.
+
+=cut
+--]]
+

Added: 7.3/trunk/squeezeplay/src/squeezeplay/share/applets/Playback/strings.txt
URL: 
http://svn.slimdevices.com/7.3/trunk/squeezeplay/src/squeezeplay/share/applets/Playback/strings.txt?rev=3167&root=Jive&view=auto
==============================================================================
--- 7.3/trunk/squeezeplay/src/squeezeplay/share/applets/Playback/strings.txt 
(added)
+++ 7.3/trunk/squeezeplay/src/squeezeplay/share/applets/Playback/strings.txt 
Tue Oct 21 01:40:17 2008
@@ -1,0 +1,53 @@
+#
+# The two letter codes are defined by ISO 639-1
+# http://en.wikipedia.org/wiki/List_of_ISO_639_codes
+
+AUDIO_PLAYBACK
+       EN      Audio Playback
+
+ENABLE_AUDIO
+       EN      Enable Playback (BETA)
+
+DEBUG_AUDIO
+       EN      Debug Playback
+
+TEST_TONES
+       EN      Test Tones
+
+FORMAT
+       EN      Format:
+
+DECODE_BUFFER
+       EN      Decode buffer:
+
+OUTPUT_BUFFER
+       EN      Output buffer:
+
+ELAPSED_SECS
+       EN      Elapsed Secs:
+
+NUM_TRACKS
+       EN      Num Tracks:
+
+DECODE_STATE
+       EN      Decode State:
+
+MULTITONE
+       EN      Multitone
+
+SINE_44.1k
+       EN      Sine 44.1k
+
+SINE_48k
+       EN      Sine 48k
+
+SINE_88.2K
+       EN      Sine 88.2k
+
+SINE_96K
+       EN      Sine 96k
+
+SINE_STOP
+       EN      Stop
+
+

Propchange: 
7.3/trunk/squeezeplay/src/squeezeplay/share/applets/Playback/strings.txt
------------------------------------------------------------------------------
    svn:executable = *

Modified: 
7.3/trunk/squeezeplay/src/squeezeplay/share/applets/SlimDiscovery/SlimDiscoveryMeta.lua
URL: 
http://svn.slimdevices.com/7.3/trunk/squeezeplay/src/squeezeplay/share/applets/SlimDiscovery/SlimDiscoveryMeta.lua?rev=3167&root=Jive&r1=3166&r2=3167&view=diff
==============================================================================
--- 
7.3/trunk/squeezeplay/src/squeezeplay/share/applets/SlimDiscovery/SlimDiscoveryMeta.lua
 (original)
+++ 
7.3/trunk/squeezeplay/src/squeezeplay/share/applets/SlimDiscovery/SlimDiscoveryMeta.lua
 Tue Oct 21 01:40:17 2008
@@ -46,9 +46,6 @@
 
 
 function registerApplet(meta)
-       local settings = meta:getSettings()
-
-
        meta:registerService("getCurrentPlayer")
        meta:registerService("setCurrentPlayer")
 
@@ -63,18 +60,14 @@
 
        meta:registerService("getPollList")
        meta:registerService("setPollList")
-
-       -- SlimDiscovery is a resident Applet
-       local slimDiscovery = appletManager:loadApplet("SlimDiscovery")
+end
 
 
-       -- FIXME See Bug 8669
-       -- SlimBrowser needs to be loaded first, so it gets notifications from
-       -- the code below.
-       appletManager:loadApplet("SlimBrowser")
+function configureApplet(meta)
+       local settings = meta:getSettings()
+       local player, server
 
-
-       local player, server
+       local slimDiscovery = appletManager:loadApplet("SlimDiscovery")
 
        -- Current server
        if settings.serverName then

Modified: 7.3/trunk/squeezeplay/src/squeezeplay/share/jive/AppletManager.lua
URL: 
http://svn.slimdevices.com/7.3/trunk/squeezeplay/src/squeezeplay/share/jive/AppletManager.lua?rev=3167&root=Jive&r1=3166&r2=3167&view=diff
==============================================================================
--- 7.3/trunk/squeezeplay/src/squeezeplay/share/jive/AppletManager.lua 
(original)
+++ 7.3/trunk/squeezeplay/src/squeezeplay/share/jive/AppletManager.lua Tue Oct 
21 01:40:17 2008
@@ -258,6 +258,8 @@
                        return nil
                end
        end
+
+       return true
 end
 
 

Modified: 7.3/trunk/squeezeplay/src/squeezeplay/share/jive/audio/Playback.lua
URL: 
http://svn.slimdevices.com/7.3/trunk/squeezeplay/src/squeezeplay/share/jive/audio/Playback.lua?rev=3167&root=Jive&r1=3166&r2=3167&view=diff
==============================================================================
--- 7.3/trunk/squeezeplay/src/squeezeplay/share/jive/audio/Playback.lua 
(original)
+++ 7.3/trunk/squeezeplay/src/squeezeplay/share/jive/audio/Playback.lua Tue Oct 
21 01:40:17 2008
@@ -79,9 +79,13 @@
        self.sentOutputUnderrunEvent = false
        self.sentAudioUnderrunEvent = false
 
-       log:info("playback init")
-       
        return obj
+end
+
+
+function free(self)
+       Decode:stop()
+       self.timer:stop()
 end
 
 

Modified: 7.3/trunk/squeezeplay/src/squeezeplay/share/jive/net/SlimProto.lua
URL: 
http://svn.slimdevices.com/7.3/trunk/squeezeplay/src/squeezeplay/share/jive/net/SlimProto.lua?rev=3167&root=Jive&r1=3166&r2=3167&view=diff
==============================================================================
--- 7.3/trunk/squeezeplay/src/squeezeplay/share/jive/net/SlimProto.lua 
(original)
+++ 7.3/trunk/squeezeplay/src/squeezeplay/share/jive/net/SlimProto.lua Tue Oct 
21 01:40:17 2008
@@ -302,22 +302,20 @@
 
 -- Create a slimproto connection object, to connect to SqueezeCenter at
 -- address. The heloPacket is sent on server (re)connection.
-function __init(self, jnt, serverip, heloPacket)
+function __init(self, jnt, heloPacket)
        -- validate the heloPacket
        assert(heloPacket.revision)
+       assert(heloPacket.mac)
+       assert(heloPacket.uuid)
 
        local obj = oo.rawnew(self, {})
 
        -- connection state UNCONNECTED / CONNECTED
        obj.state          = UNCONNECTED
-       obj.serverip       = serverip
 
        -- helo packet sent on connection
        obj.heloPacket     = heloPacket
-
-       local uuid, mac = jnt:getUUID()
-       obj.heloPacket.mac = string.lower(mac)
-       obj.heloPacket.uuid = uuid
+       obj.heloPacket.mac = string.lower(obj.heloPacket.mac)
 
        obj.statusCallback = _defaultStatusCallback
 
@@ -326,7 +324,6 @@
 
        -- network state
        obj.jnt            = jnt
-       obj.socket         = SocketTcp(jnt, serverip, PORT, "SlimProto")
 
        -- reconnect timer
        obj.reconnectTimer = Timer(0, function() _handleTimer(obj) end, true)
@@ -348,7 +345,9 @@
 
 
 -- Open the slimproto connection to SqueezeCenter.
-function connect(self)
+function connect(self, serverip)
+       assert(serverip)
+
        local pump = function(NetworkThreadErr)
                if NetworkThreadErr then
                        return _handleDisconnect(self, NetworkThreadErr)
@@ -405,6 +404,9 @@
                end
        end
 
+       self.serverip = serverip
+       self.socket = SocketTcp(self.jnt, serverip, PORT, "SlimProto")
+
        -- connect
        self.socket:t_connect()
        self.socket:t_addRead(pump, 0) -- no timeout
@@ -418,8 +420,21 @@
 
 -- Disconnect from SqueezeCenter.
 function disconnect(self)
+       if self.state ~= CONNECTED then
+               return
+       end
+
        self.state = UNCONNECTED
        self.socket:close()
+
+       self.socket = nil
+       self.serverip = nil
+end
+
+
+-- Return true if slimproto is connected
+function isConnected(self)
+        return self.state == CONNECTED
 end
 
 

Modified: 7.3/trunk/squeezeplay/src/squeezeplay/share/jive/slim/SlimServer.lua
URL: 
http://svn.slimdevices.com/7.3/trunk/squeezeplay/src/squeezeplay/share/jive/slim/SlimServer.lua?rev=3167&root=Jive&r1=3166&r2=3167&view=diff
==============================================================================
--- 7.3/trunk/squeezeplay/src/squeezeplay/share/jive/slim/SlimServer.lua 
(original)
+++ 7.3/trunk/squeezeplay/src/squeezeplay/share/jive/slim/SlimServer.lua Tue 
Oct 21 01:40:17 2008
@@ -335,6 +335,11 @@
 
 -- Update server on start up
 function updateInit(self, init)
+       if serverList[self.id] then
+               -- already initialized
+               return
+       end
+
        self.ip = init.ip
        self.mac = init.mac
 
@@ -695,7 +700,7 @@
                                -- XXXX manage pool of connections to remote 
server
                                local uri  = req:getURI()
                                local http = SocketHttp(self.jnt, uri.host, 
uri.port, uri.host)
-                                                   
+ 
                                http:fetch(req)
                        end
 

Modified: 7.3/trunk/squeezeplay/src/squeezeplay/src/audio/decode/decode.c
URL: 
http://svn.slimdevices.com/7.3/trunk/squeezeplay/src/squeezeplay/src/audio/decode/decode.c?rev=3167&root=Jive&r1=3166&r2=3167&view=diff
==============================================================================
--- 7.3/trunk/squeezeplay/src/squeezeplay/src/audio/decode/decode.c (original)
+++ 7.3/trunk/squeezeplay/src/squeezeplay/src/audio/decode/decode.c Tue Oct 21 
01:40:17 2008
@@ -532,6 +532,11 @@
        lua_pushinteger(L, decode_num_tracks_started);
        lua_setfield(L, -2, "tracksStarted");
 
+       if (decoder) {
+               lua_pushinteger(L, decoder->id);
+               lua_setfield(L, -2, "decoder");
+       }
+
        fifo_unlock(&decode_fifo);
 
 

Modified: 7.3/trunk/squeezeplay/src/squeezeplay_desktop/Makefile.am
URL: 
http://svn.slimdevices.com/7.3/trunk/squeezeplay/src/squeezeplay_desktop/Makefile.am?rev=3167&root=Jive&r1=3166&r2=3167&view=diff
==============================================================================
--- 7.3/trunk/squeezeplay/src/squeezeplay_desktop/Makefile.am (original)
+++ 7.3/trunk/squeezeplay/src/squeezeplay_desktop/Makefile.am Tue Oct 21 
01:40:17 2008
@@ -5,5 +5,3 @@
 include Makefile.am.jive-install.include
 
 install-data-local: jive-static-install
-       ##eliminate anything we still want private
-       rm -rf $(JIVE_BUILD_DIR)/applets/TestDecode/

Modified: 7.3/trunk/squeezeplay/src/squeezeplay_desktop/Makefile.in
URL: 
http://svn.slimdevices.com/7.3/trunk/squeezeplay/src/squeezeplay_desktop/Makefile.in?rev=3167&root=Jive&r1=3166&r2=3167&view=diff
==============================================================================
--- 7.3/trunk/squeezeplay/src/squeezeplay_desktop/Makefile.in (original)
+++ 7.3/trunk/squeezeplay/src/squeezeplay_desktop/Makefile.in Tue Oct 21 
01:40:17 2008
@@ -402,7 +402,6 @@
            | tar -C $(JIVE_BUILD_DIR) -pxf -
 
 install-data-local: jive-static-install
-       rm -rf $(JIVE_BUILD_DIR)/applets/TestDecode/
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
 .NOEXPORT:

_______________________________________________
Jive-checkins mailing list
[email protected]
http://lists.slimdevices.com/cgi-bin/mailman/listinfo/jive-checkins

Reply via email to