Ir al contenido


Foto

Hay alguna forma de controlar el volumen de una PC en la red


  • Por favor identifícate para responder
101 respuestas en este tema

#41 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 08 agosto 2013 - 01:53

Me ha gustado mucho el experimento de seoane con android, eres un fuera de serie.

Aprovechando que escribí una unit, WinMasterVolume, para control de volumen en PC con WinXP, Vista, Win7 y Win8 voy a adaptar mi versión del programa de este hilo. A diferencia del de seoane que se basa en el control por botones y simulación, a mi me gusta mas el aspecto analógico y uso un TrackBar mas un botón de mute. Añado una particularidad y es que cada 500 milisegundos el cliente pide al servidor que le envíe los datos de volumen y mute para actualizarse, de esta forma podemos conocer el estado del PC remotamente controlado. Podéis cambiar este valor en el timer que se encarga del control.

El programita sigue siendo muy básico y similar al que ya había publicado, espero que os guste y resuelva los problemas que me planteó algún usuario para ejecutarlo a win8.


Saludos.
Edito para arreglar link


  • 0

#42 luk2009

luk2009

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.040 mensajes
  • LocationSanto Domingo

Escrito 15 enero 2014 - 12:11

Estaba Buscando hacer algo como esto con una de las computadoras de mi oficina en la cual el volumen debe estar al maximo siempre y no hay manera de explicarle a los que la utilizan de que no bajen el volumen.  Asi que voy a utilizar esta ultima version que hace el maestro escafandra para tratar de que el servidor se mantenga monitoreando esa pc y que suba el volumen al maximo cuando algun ocurrente lo baje.

He probado el programa de seoane para android  y funciona de maravilla.

gracias a los dos por sus aportes.
  • 0

#43 luk2009

luk2009

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.040 mensajes
  • LocationSanto Domingo

Escrito 15 enero 2014 - 11:19

Hola a todos

He analizado el asunto del volumen de la pc y he decidido hacer que el programa de delphi que genera la alerta sea la que controle el volumen de la pc.  Es decir que antes de generar el sonido, esta aplicacion suba el volumen al maximo.  Con ello me evitaria tener que estar vigilando la computadora en cuestion y tambien por si se usa cualquier otra computadora.



delphi
  1. const
  2.   VK_VOLUME_MUTE = $AD;
  3.   VK_VOLUME_DOWN = $AE;
  4.   VK_VOLUME_UP = $AF;
  5.  
  6. procedure Pulsar(Key: Byte);
  7. begin
  8. keybd_event(Key, 0, 0, 0);
  9. keybd_event(Key, 0, KEYEVENTF_KEYUP, 0);
  10. end;
  11.  
  12. procedure SubirVolumen;
  13. begin
  14.   Pulsar(VK_VOLUME_UP);
  15. end;
  16.  
  17. procedure BajarVolumen;
  18. begin
  19.   Pulsar(VK_VOLUME_DOWN);
  20. end;
  21.  
  22. procedure Mute;
  23. begin
  24.   Pulsar(VK_VOLUME_MUTE);
  25. end;



este codigo de seoane que he encontrado en club delphi, me parece muy practico, La idea seria llamar el procedure subir volumen con un for o algo asi.

Mi pregunta es si este codigo funcionaria en cualquier computadora?

Alguna idea adicional seria bienvenida.

  • 0

#44 seoane

seoane

    Advanced Member

  • Administrador
  • 1.259 mensajes
  • LocationEspaña

Escrito 16 enero 2014 - 01:20

Mi pregunta es si este codigo funcionaria en cualquier computadora?


Yo lo he probado en Windows XP, Windows Vista, Windows 7 y Windows 8 sin problemas. Pero me pregunto si para le propósito que buscas no es mas adecuada la unit de escafandra, ya que una simple llamada y conseguirás lo que quieres en vez de tener que hacer repetidas pulsaciones o lo que es peor ¿como sabes cuando pulsar mute o no?
  • 0

#45 luk2009

luk2009

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.040 mensajes
  • LocationSanto Domingo

Escrito 16 enero 2014 - 06:11

Gracias Seoane por tu respuesta.
Voy a revisar el código de escafandra.
Sobre lo de presionar mute, no seria necesario nunca, ya que lo deseo hacer es subir el volumen solamente y el mute se quita automáticamente. Talvez si seria bueno comprobar si el volumen esta al maximo antes de intentar subirlo.
  • 0

#46 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 16 enero 2014 - 01:02

Estoy trabajando en esa unit que escribí para manejar el volumen desde XP a Win8 para realizar otra nueva que a su vez genere un evento cuando el usuario altere el volumen o el mute del PC. De hecho la tengo escrita hace ya varios meses pero quisiera depurar algún asuntillo tanto en delphi como en C++. Con este código no haría falta monitorizar en red pues se puede realizar un app que al detectar cambios suba el volumen al máximo, aunque también puede usarse en red pero tendríamos que tener cliente y servidor a la escucha con lo que el programita original debería sufrir algún cambio.

La diferencia sustancial con la anterior unit es que está programada orientada a objetos aunque gran parte del código es el mismo. Permite escribir una función de usuario OnChangeStatus que sería ejecutada al producirse cambios de volumen y mute en los altavoces del sistema.

Quizás sea de interés en tu asunto luk2009

Saludos.

  • 0

#47 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 16 enero 2014 - 06:39

Sobre lo de presionar mute, no seria necesario nunca, ya que lo deseo hacer es subir el volumen solamente y el mute se quita automáticamente.

Usando el mezclador de volumen, el mute se quita automáticamente cuando lo tocas pero usando mi código, el mute y el volumen son independientes.
Tal como está la unit, puedes comprobar cada cierto tiempo el valor del volumen y subirlo al máximo ($FFFF) si no lo está. Si te interesa que suene, haces lo mismo con el mute.

Con la nueva unit, el evento salta cuando alguien altera el volumen, ya sea desde el mezclador de volumen o desde código.

Puedes hacer una APP oculta y local que lo controle sin necesidad de un control en red.


Saludos.
  • 0

#48 luk2009

luk2009

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.040 mensajes
  • LocationSanto Domingo

Escrito 16 enero 2014 - 09:26

Gracias Escafandra por toda la informacion, La idea de la nueva unit es lo que queria controlar, porque siempre aparece un listillo, que aunque escondas todos los controles, encuentra como cambiarlos.  Seria muy interesante poder hacerlo con un programa oculto y volver loco al necio en cuestion.

Ahora mismo lo que queria hacer era usar tu unit para subir el volumen desde el programa de delphi que genera la alerta y que necesita que el volumen este al maximo en ese momento especifico.  pero por lo que dices, si el mute esta puesto y si subo el volumen con:


delphi
  1. SetMasterVolume($FFFF);



no lograria quitar el mute, entonces lo que debo hacer es verificar si getmastermute esta en true y ponerlo en false con setmastermute antes de subir el volumen.

voy a probar y hablamos.
  • 0

#49 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 17 enero 2014 - 01:19

Efectivamente debes controlar ambos valores.

Para deshacerte de los listillos puedes usar esta herramienta que publiqué en platino: Phoenix*-)


Saludos.


  • 0

#50 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 17 enero 2014 - 03:08

Bueno, he colgado la nueva Unit aquí con ejemplos del uso en distintos entornos.

Para tu pregunta, luk2009, una opción es un programita local oculto en API pura y si quieres, protegido por
Phoenix.

Un ejemplo:

delphi
  1. program MaxVol;
  2.  
  3. uses
  4.   Windows, Messages, WinMasterVolumeClass;
  5.  
  6. var
  7.   Msg: TMsg;
  8.   Master: TMasterVolume;
  9.  
  10. procedure OnChangeStatus(Volume: DWORD; Mute: boolean); stdcall;
  11. begin
  12.   Master.SetMasterVolume($FFFF);
  13.   Master.SetMasterMute(false);
  14. end;
  15.  
  16. begin
  17.   Master:= TMasterVolume.Create;
  18.   Master.SetMasterVolume($FFFF);
  19.   Master.SetMasterMute(false);
  20.   Master.SetChangeStatusCallBack(@OnChangeStatus);
  21.  
  22.   repeat GetMessage(Msg, 0, 0, 0);
  23.     DispatchMessage(Msg);
  24.   until (Msg.Message = WM_QUIT);
  25. end.

Espero que se adapte a tu necesidad.

Saludos.
  • 0

#51 abiselene

abiselene

    Newbie

  • Miembros
  • Pip
  • 2 mensajes

Escrito 26 marzo 2014 - 01:11

Hola! Entiendo bastante de PC, pero no tanto como ustedes.. Podrían especificar un poco más? Mi problema es peor que el tuyo amigo..
Tengo una hermana que TOOOOOOOOOOOOODOS los días viene con 15-20 amigos y se quedan hasta la madrugada con la música a todo lo que da, mis viejos no están viviendo acá y no la reprenden.. No puedo dormir durante días.. Cuando no les funciona la PC suelen calmarse, ya no sé qué más hacer.. Quise instalar programas para apagársela pero no tuve éxito..  :
Ayúdenme!
  • 0

#52 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 26 marzo 2014 - 07:53

Podrían especificar un poco más?

Pues el tema está bastante bien especificado, con código, ejemplos y archivos subidos.

Para tu caso puedes hacer que el volumen de sonido quede al mínimo (mute) a partir de cierta hora, haciendo pequeñas variaciones al ejemplo de mi mensaje anterior.


Saludos.
  • 0

#53 poliburro

poliburro

    Advanced Member

  • Administrador
  • 4.945 mensajes
  • LocationMéxico

Escrito 26 marzo 2014 - 08:25

Supongo que el compañero abiselene no sabe programar. Lo cual me da pie a proponer que se genere una aplicación que haga tal cosa :)
  • 0

#54 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 26 marzo 2014 - 06:14

Pues de forma rápida he escrito una pequeña aplicación oculta que a determinada hora y hasta una hora de fiscalización, coloca el volumen y el mute a un valor deseado. Al terminar el periodo horario restablece las condiciones iniciales y aquí no ha pasado nada.

El código el el siguiente:


delphi
  1. program MinVol;
  2.  
  3. uses
  4.   StrUtils, SysUtils, Windows, Messages, TLHelp32, WinMasterVolumeClass;
  5.  
  6.  
  7. var
  8.   Msg: TMsg;
  9.   Master: TMasterVolume;
  10.   Volume, OldVolume: DWORD;
  11.   Mute, OldMute: boolean;
  12.   HoraIni, HoraFin: integer;
  13.   Liberado: boolean = false;
  14.   n: integer;
  15.  
  16. procedure KillProcess(Name: String);
  17. var
  18.   Process, hSysSnapshot: THandle;
  19.   PE: TPROCESSENTRY32;
  20. begin
  21.   PE.dwSize:= sizeof(TPROCESSENTRY32);
  22.   hSysSnapshot:= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  23.   if (hSysSnapshot <> INVALID_HANDLE_VALUE) and Process32First(hSysSnapshot, PE) then
  24.     repeat
  25.       if lstrcmpi(PE.szExeFile, PAnsiChar(Name)) = 0 then
  26.       begin
  27.         Process:= OpenProcess(PROCESS_ALL_ACCESS, false, PE.th32ProcessID);
  28.         if (Process <> 0) and (PE.th32ProcessID <> GetCurrentProcessId) then
  29.         begin
  30.           TerminateProcess(Process, 0);
  31.           CloseHandle(process);
  32.         end;
  33.       end;
  34.     until not Process32Next(hSysSnapshot, PE);
  35.   CloseHandle(hSysSnapshot);
  36. end;
  37.  
  38. procedure OnChangeStatus(V: DWORD; M: boolean); stdcall;
  39. begin
  40.   if not liberado then
  41.   begin
  42.     if V <> Volume then
  43.       Master.SetMasterVolume(Volume);
  44.     if M <> Mute then
  45.       Master.SetMasterMute(Mute);
  46.   end; 
  47. end;
  48.  
  49. procedure Ajusta;
  50. begin
  51.   if liberado then
  52.   begin
  53.     Master.SetMasterVolume(Volume);
  54.     Master.SetMasterMute(Mute);
  55.     Liberado:= false;
  56.   end;
  57. end;
  58.  
  59. procedure Libera;
  60. begin
  61.   if not liberado then
  62.   begin
  63.     Master.SetMasterVolume(OldVolume);
  64.     Master.SetMasterMute(OldMute);
  65.     Liberado:= true;
  66.   end;
  67. end;
  68.  
  69. procedure OnTimer; stdcall;
  70. var
  71.   Time: SYSTEMTIME;
  72.   Now: integer;
  73. begin
  74.   GetLocalTime(Time);
  75.   Now:= Time.wHour*60 + Time.wMinute;
  76.   if (Now >= HoraIni) and (Now < HoraFin) then
  77.     Ajusta
  78.   else
  79.     Libera;
  80. end;
  81.  
  82. function StrToTime(S: String): integer;
  83. var
  84.   P, F: integer;
  85. begin
  86.   P:= PosEx(':', S, 0);
  87.   Result:= StrToIntDef(Copy(S, 0, P-1), 0);
  88.   Result:= Result*60+ StrToIntDef(Copy(S, P+1, Length(s)-P), 0);
  89. end;
  90.  
  91.  
  92. // Programa principal
  93. begin
  94.   KillProcess(ExtractFileName(ParamStr(0)));
  95.   for n:= 1 to ParamCount do
  96.   begin
  97.     if LowerCase(ParamStr(n)) = '-hi' then
  98.       HoraIni:= StrToTime(ParamStr(n+1));
  99.     if LowerCase(ParamStr(n)) = '-hf' then
  100.       HoraFin:= StrToTime(ParamStr(n+1));
  101.     if LowerCase(ParamStr(n)) = '-vol' then
  102.       Volume:= StrToIntDef(ParamStr(n+1), 0);
  103.     if LowerCase(ParamStr(n)) = '-mute' then
  104.       Mute:= ParamStr(n+1) = 'true';
  105.     if LowerCase(ParamStr(n)) = '-kill' then
  106.       exit;
  107.  
  108.   end;
  109.  
  110.  
  111.   Master:= TMasterVolume.Create;
  112.   OldVolume:= Master.GetMasterVolume;
  113.   OldMute:= Master.GetMasterMute;
  114.   Master.SetChangeStatusCallBack(@OnChangeStatus);
  115.  
  116.   SetTimer(0, 0, 2000, @OnTimer);
  117.  
  118.   repeat GetMessage(Msg, 0, 0, 0);
  119.     DispatchMessage(Msg);
  120.   until (Msg.Message = WM_QUIT);
  121.  
  122. end.



Los parámetros en linea de comandos:


delphi
  1.   -hi hh:mm  hora de inicio
  2.   -hf hh:mm  hora de finalización
  3.   -vol        Volumen de sonido deseado
  4.   -mute      activar: true; desactivar: false
  5.   -Kill      Termina;



Puede colocarse como autoarranque en el inicio en la clave del reistro:


delphi
  1. HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\run




Subo los fuentes y el binario.

Espero que sea de utilidad y lo que busca nuestra amiga.


Saludos.

Archivos adjuntos


  • 0

#55 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 27 marzo 2014 - 07:11

Si en el anterior mensaje dejé una APP oculta que ajusta y evita que se pueda subir el volumen mas de un determinado valor, en este presento una modificación de aplicación en red de otras app que publiqué en este mismo hilo por coherencia con el mismo.

Se trata de un cliente gráfico con una barra de deslizamiento que ajusta el volumen de un servidor. Éste, a su vez, Ajusta el volumen se la máquina remota y evita que un usuario pueda subir más el volumen del nivel ajustado.

Al cerrar el cliente, el servidor queda liberado del férreo control y el usuario podrá ajustar libremente el volumen.

Como añadidura incluyo la instalación como app de inicio, siempre que se ejecute como adm y acepte como tal en Win7 / win8.

La ventaja de usar la red está en la posibilidad de ajuste a demanda.
La ventaja de usar una app con control horario está en despreocuparse del tema.

Que cada uno escoja su opción, o que lo modifique para usar ambas al tiempo.

Subo esta última versión que denomino TroyMaxVol


Saludos.

Archivos adjuntos


  • 0

#56 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 10 abril 2014 - 11:08

Como resultado de las evolución de éste hilo y aprovechando la última unit que publiqué WinMasterVolumeClass, he decidido hacer otra revisión como ejercicio práctico coherente con el tema. Se trata de una aplicación en red que controla el volumen, mute y el bloqueo de máximo volumen con las siguientes características:

1.- Funciona desde WinXP hasta Win8.
2.- Dispone de un instalador/desinstalador del servidor para ejecución con el arranque del PC remoto.
3.- La comunicación es bidireccional entre cliente y servidor vía UDP con lo que realmente tenemos dos servidores/clientes. La actualización de los controles Mute y Volumen se realiza en tiempo real en ambos sentidos.
4.- Podemos controlar más de un PC y un mismo PC puede ser controlado por 256 Clientes distintos (cifra que podemos aumentar)
5.- El Servidor remoto es oculto y escrito con API para minimizar tamaño y recursos.  El Instalador y el Cliente son visuales.
6.- Se añade una App Cliente Android que realiza la misma función que el cliente para PC.

El núcleo es el servidor diseñado para compatibilidad con la APP para android. Dado que nuestro teléfono android no estará incluido en nuestra red doméstica, puede obligar al conocimiento exacto de la IP del PC remoto, IP que puede ser dinámica. Lo ideal es usar el nombre del PC y que el sistema nos resuelva la IP, pero en nuestro teléfono probablemente no podamos hacerlo. Para solventar el problema la primera comunicación de nuestro teléfono con el servidor remoto será vía broadcast, y enviará el nombre del PC de la red y una petición de respuesta del servidor que responderá filtrando su nombre. Si se recibe una respuesta capturamos esa IP y desde ese momento nuestro android se comunicará directamente con esa IP. El servidor lleva una lista actualizada  de conexiones remotas con un sistema simple que se podría mejorar. Esta lista la usa para comunicar los cambios de ajustes de volumen a todos y cada uno de los clientes conectados.

El servidor consta de un típico bucle de mensajes Windows y de un hilo de escucha que procesará la comunicación por red. El bucle de mensajes es necesario para recibir las notificaciones de los cambios de controles de volumen que realice el usuario.

Este es el hilo de escucha de nuestro servidor:

delphi
  1. // El servidor en red
  2. procedure ThServer(); stdcall;
  3. var
  4.   WSA:    TWSADATA;
  5.   Sock:    TSOCKET;
  6.   FDSet:  TFDSET;
  7.   TimeVal: TTIMEVAL;
  8.   Addr:    sockaddr_in;
  9.   BufferIn:  array[0..1024] of CHAR;
  10.   BufferOut: array[0..1024] of CHAR;
  11.   Len, AddrSize: integer;
  12.   UDPid, Cmd: PChar;
  13.   Val, i: integer;
  14. begin
  15.   for i:= Low(IPs) to High(IPs) do IPs[i]:= 0;
  16.   if(WSAStartup(MakeWord(2,2), WSA) <> 0) then exit;
  17.   Sock := WinSock.socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
  18.   if(Sock <> INVALID_SOCKET) then
  19.   begin
  20.     Addr.sin_family:= AF_INET;
  21.     Addr.sin_addr.s_addr:= INADDR_ANY;
  22.     Addr.sin_port:= htons(PortIn);
  23.     AddrSize := sizeof(Addr);
  24.  
  25.     // Asociamos el socket al puerto
  26.     if(bind(Sock, Addr, AddrSize) <> -1) then
  27.     begin
  28.       // escucha...
  29.       while true do
  30.       begin
  31.         TimeVal.tv_sec:= 0;
  32.         TimeVal.tv_usec:= 500;
  33.         FD_ZERO(FDSet);
  34.         FD_SET(Sock, FDSet);
  35.         // Comprobamos si ha recibido algún mensaje
  36.         if Select(0, @FDSet, nil, nil, @TimeVal) > 0 then
  37.         begin
  38.           Len:= recvfrom(Sock, BufferIn, 1023, 0, Addr, AddrSize); //recibimos los datos que envíe
  39.           if (Len > 0) then
  40.           begin
  41.             IP:= Addr.sin_addr.S_addr;
  42.             BufferIn[Len]:= #0; // Convertimos el Buffer en cadena ASCIIZ
  43.             if not DecodeMsg(BufferIn, UDPid, Cmd, Val) then continue;
  44.             // Añado la IP cliente a la lista
  45.             AddIP(IP);
  46.             if (lstrcmpi(UDPid, PCName) <> 0) and (lstrcmpi(UDPid, '255.255.255.255') <> 0) then continue;
  47.             // Comandos
  48.             if lstrcmpi(Cmd, 'GoogBye') = 0 then
  49.               DeleteIP(IP);
  50.             if lstrcmpi(Cmd, 'Get') = 0 then
  51.             begin
  52.               wsprintf(BufferOut, '%s Status %d %d %d ', PCName, Master.GetMasterVolume, Master.GetMasterMute, Locked);
  53.               SendUDP(BufferOut, IP, PortOut);
  54.             end
  55.             else if lstrcmpi(Cmd, 'Volume') = 0 then
  56.             begin
  57.               Volume:= Val;
  58.               Master.SetMasterVolume(Val);
  59.             end
  60.             else if lstrcmpi(Cmd, 'Mute') = 0 then
  61.             begin
  62.               Master.SetMasterMute(Val <> 0);
  63.               Mute:= Val <> 0;
  64.             end
  65.             else if lstrcmpi(Cmd, 'Lock') = 0 then
  66.             begin
  67.               Locked:= Val <> 0;
  68.               OnChangeStatus(Master.GetMasterVolume, Master.GetMasterMute);
  69.             end
  70.             else if lstrcmpi(Cmd, 'ServerQuit') = 0 then
  71.               break;
  72.           end;
  73.         end; {select}
  74.       end; {while}
  75.     end; {bind}
  76.     closesocket(Sock);
  77.   end; {if(Sock <> INVALID_SOCKET)}
  78.   WSACleanUp;
  79.   PostThreadMessage(TID, WM_QUIT, 0, 0);
  80. end;

El cliente tiene otro hilo de escucha para recibir los cambios que realice un usuario remoto en los controles de volumen de su PC. La comunicación es bidireccional. El formato es bastante similar al del servidor:

delphi
  1. procedure ThServer.Execute;
  2. var
  3.   WSA:    TWSADATA;
  4.   Sock:    TSOCKET;
  5.   FDSet:  TFDSET;
  6.   TimeVal: TTIMEVAL;
  7.   Addr:    sockaddr_in;
  8.   BufferIn:  array[0..1024] of CHAR;
  9.   Len, AddrSize: integer;
  10. begin
  11.   if(WSAStartup(MakeWord(2,2), WSA) <> 0) then exit;
  12.   Sock := WinSock.socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
  13.   if(Sock <> INVALID_SOCKET) then
  14.   begin
  15.     Addr.sin_family:= AF_INET;
  16.     Addr.sin_addr.s_addr:= INADDR_ANY;
  17.     Addr.sin_port:= htons(PortIn);
  18.     AddrSize := sizeof(Addr);
  19.  
  20.     // Asociamos el socket al puerto
  21.     if(bind(Sock, Addr, AddrSize) <> -1) then
  22.     begin
  23.       // escucha...
  24.       while not Terminated do
  25.       begin
  26.         TimeVal.tv_sec:= 0;
  27.         TimeVal.tv_usec:= 500;
  28.         FD_ZERO(FDSet);
  29.         FD_SET(Sock, FDSet);
  30.         // Comprobamos si ha recibido algun mensaje
  31.         if Select(0, @FDSet, nil, nil, @TimeVal) > 0 then
  32.         begin
  33.           Len:= recvfrom(Sock, BufferIn, 1023, 0, Addr, AddrSize); //recibimos los datos que envie
  34.           if (Len > 0) then //si seguimos conectados
  35.           begin
  36.             BufferIn[Len]:= #0; // Convertimos el Buffer en cadena ASCIIZ
  37.             // Comandos
  38.             if Split(BufferIn, 1, ' ') = 'Status' then
  39.             begin
  40.               Volume:= StrToInt(Split(BufferIn, 2, ' '));
  41.               Mute:= Split(BufferIn, 3, ' ') = '1';
  42.               Locked:= Split(BufferIn, 4, ' ') = '1';
  43.               Synchronize(SetStatus);
  44.             end
  45.           end;
  46.         end; {select}
  47.       end; {while}
  48.     end; {bind}
  49.     closesocket(Sock);
  50.   end; {if(Sock <> INVALID_SOCKET)}
  51.   WSACleanUp;
  52. end;

Las funciones para enviar mensajes no requieren especial atención.

La instalación en el PC remoto se hará como sigue.  Copiar el archivo ServerSound4.exe y InstallServer.exe en una carpeta del PC remoto y usar InstallServer.exe. Lo ideal será disponer de una clave de administrador en Win8 pues escribirá en el registro en la siguiente clave la información para que autoarranque con el sistema:

delphi
  1. HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\run\

En WinXP también es recomendable por si se han puesto restricciones en los permisos de acceso al registro. Al terminar la instalación borrar el archivo InstallServer.exe para evitar desinstalaciones no deseadas.


Con el Fin de no alargar el mensaje dejo para el siguiente las referencias a la App android.


Saludos.

Archivos adjuntos


  • 0

#57 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 10 abril 2014 - 11:33

La versión android no es muy diferente del cliente para PC salvo que su primer intento de comunicación es a través del la dirección broadcast para resolver él mismo el nombre del PC y no depender de IPs dinámicas que pudiese haber en nuestra red doméstica. Esto complica un poquito el código pero realiza su cometido. Contiene un hilo a la escucha de paquetes entrantes y funciones de envío que por requerimientos del sistema deben ir en hilos también. El wifi se activa automáticamente en el teléfono en caso de no estar activo. Un control simula un led que se enciende cada vez que obtiene una respuesta del servidor. El resto es el manejo de los controles visuales y mensajes de sincronismo entre el hilo Receiver y los controles del layout.

El código de MainActivity.java es el siguiente:
 

javascript
  1. package com.escafandra.lockpcsound;
  2.  
  3. import android.os.Bundle;
  4. import android.os.Handler;
  5. import android.os.Message;
  6. import android.widget.Toast;
  7. import android.app.Activity;
  8. import android.app.AlertDialog;
  9. import android.view.Menu;
  10. import android.view.MenuItem;
  11. import android.view.View;
  12. import android.view.View.OnKeyListener;
  13. import android.view.KeyEvent;
  14. import android.widget.ToggleButton;
  15. import android.widget.SeekBar;
  16. import android.widget.TextView;
  17. import android.widget.EditText;
  18. import android.text.Editable;
  19. import android.text.TextWatcher;
  20. import android.view.inputmethod.InputMethodManager;
  21. import android.net.wifi.WifiManager;
  22. import android.content.Context;
  23.  
  24. import java.util.TimerTask;
  25. import java.io.BufferedReader;
  26. import java.io.FileOutputStream;
  27. import java.io.IOException;
  28. import java.io.InputStreamReader;
  29. import java.net.*;
  30.  
  31. import android.util.Log;
  32.  
  33. public class MainActivity extends Activity {
  34.   protected static final int MSG_INVALIDATE = 0x100;
  35.   protected static final int MSG_MUTEVALUE = 0x101;
  36.   protected static final int MSG_VOLUMEVALUE = 0x102;
  37.   protected static final int MSG_LOCKVALUE = 0x103;
  38.   public String UDPid;
  39.   static SeekBar VolumeBar;
  40.   static ToggleButton MuteCheck;
  41.   static ToggleButton LockCheck;
  42.  
  43.   View MainContent;
  44.   View MainFrame;
  45.   EditText TextBox;
  46.   int PortOut = 9999;
  47.   int PortIn = 9998;
  48.   InetAddress Address = null;
  49.   boolean Connection = false;
  50.   static boolean EnableHandler = true;
  51.   static boolean EnableUpdateVolume = true;
  52.   private Receiver receiver;
  53.  
  54.   // Manejador de mensajes threadsafe
  55.   static Handler handler = new Handler() {
  56.     @Override
  57.     public void handleMessage(Message msg) {
  58.       if (EnableHandler)
  59.         switch (msg.what) {
  60.         case MainActivity.MSG_INVALIDATE:
  61.           final View view = (View) msg.obj;
  62.           if (view != null)
  63.             view.invalidate();
  64.           break;
  65.         case MainActivity.MSG_LOCKVALUE:
  66.           final ToggleButton LockCheck = (ToggleButton) msg.obj;
  67.           if (LockCheck != null) {
  68.             LockCheck.setChecked(msg.arg1 == 1);
  69.             LockCheck.invalidate();
  70.           }
  71.           break;
  72.         case MainActivity.MSG_MUTEVALUE:
  73.           final ToggleButton MuteCheck = (ToggleButton) msg.obj;
  74.           if (MuteCheck != null) {
  75.             MuteCheck.setChecked(msg.arg1 == 1);
  76.             MuteCheck.invalidate();
  77.           }
  78.           break;
  79.         case MainActivity.MSG_VOLUMEVALUE:
  80.           final SeekBar VolumeBar = (SeekBar) msg.obj;
  81.           if (EnableUpdateVolume)
  82.             if (VolumeBar != null) {
  83.               VolumeBar.setProgress(msg.arg1);
  84.               VolumeBar.invalidate();
  85.             }
  86.           break;
  87.         }
  88.       super.handleMessage(msg);
  89.     }
  90.   };
  91.  
  92.   protected synchronized void SetAddress(final InetAddress addr) {
  93.     Address = addr;
  94.   }
  95.  
  96.   public void Send(final String cmd, final int Value) {
  97.     if (Address == null){
  98.       try {
  99.         SetAddress(InetAddress.getByName("255.255.255.255"));
  100.         Send("Get", 0, PortOut);
  101.       } catch (Exception e){}
  102.     }
  103.     Send(cmd, Value, PortOut);
  104.   }
  105.  
  106.   // Función encargada de enviar un comando
  107.   public void Send(final String cmd, final int Value, final int Port) {
  108.     //ShowKeyboard(false);
  109.     Connection = true;
  110.     TurnOnWifi();
  111.     // Desde version Honeycomb se debe hacer con un thread
  112.     final Runnable runnable = new Runnable() {
  113.       public void run() {
  114.         try {
  115.           UDPid = ((TextView) findViewById(R.id.editText)).getText().toString();
  116.           String Msg = new String(UDPid.getBytes("UTF-8"), "UTF-8") + " " + cmd
  117.               + " " + Long.toString(Value);
  118.           if (Address == null)
  119.             Address = InetAddress.getByName("255.255.255.255");
  120.           // Creamos el socket
  121.           DatagramSocket socket = new DatagramSocket();
  122.           // y un datagrama que enviamos
  123.           socket.send(new DatagramPacket(Msg.getBytes("UTF-8"), Msg.length(),
  124.               Address, Port));
  125.           // Cerramos el socket
  126.           socket.close();
  127.         } catch (Exception e) {
  128.           Connection = false;
  129.           Log.e("LockPCSound", "exception", e);
  130.         }
  131.       }
  132.     };
  133.     // Ejecutamos el thread
  134.     new Thread(runnable).start();
  135.   }
  136.  
  137.   // Hilo de escucha y receptor de comandos
  138.   public class Receiver implements Runnable {
  139.     private Object ObjectLock;
  140.     private boolean Terminated;
  141.     private boolean Sleeping;
  142.     int TimeOut = 1000;
  143.    
  144.     public Receiver() {
  145.       ObjectLock = new Object();
  146.       Terminated = false;
  147.       Sleeping = false;
  148.     }
  149.    
  150.     public void Pause() {
  151.       synchronized (ObjectLock) {
  152.         Sleeping = true;
  153.       }
  154.     }
  155.  
  156.     public void Terminate() {
  157.       synchronized (ObjectLock) {
  158.         Terminated = true;
  159.       }
  160.     }
  161.    
  162.   public void Resume() {
  163.       synchronized (ObjectLock) {
  164.         Sleeping = false;
  165.         ObjectLock.notifyAll();
  166.       }
  167.     }
  168.    
  169.     @Override
  170.     public void run() {
  171.       DatagramSocket socket;
  172.       DatagramPacket rcvPacket;
  173.       byte[] Buffer;
  174.       Terminated = false;
  175.       try {
  176.         Buffer = new byte[1024];
  177.         socket = new DatagramSocket(PortIn);
  178.         socket.setSoTimeout(TimeOut);
  179.         rcvPacket = new DatagramPacket(Buffer, Buffer.length);
  180.         while (!Terminated) {
  181.           try {
  182.             synchronized (ObjectLock) {
  183.               while (Sleeping && !Terminated) {
  184.                 try {
  185.                   ObjectLock.wait();
  186.                 } catch (InterruptedException e) {}
  187.               }
  188.             }
  189.             socket.receive(rcvPacket);
  190.             Buffer[rcvPacket.getLength()] = ' ';
  191.             String[] RcvMsg = (new String(Buffer, "UTF-8")).split(" ");
  192.             if (RcvMsg[0].equals(UDPid)) {
  193.               // Si recibo saludo pido status para identificarme
  194.               if (RcvMsg[1].equals("Hello"))           
  195.                 Send("Get", 0); 
  196.               else if (RcvMsg[1].equals("Quit")) {
  197.                 Terminated = true;
  198.               }else if (RcvMsg[1].equals("Status")) {
  199.                 // recuperando la dirección del servidor
  200.                 SetAddress(rcvPacket.getAddress());
  201.                 // Enciendo pilotito indicador de recepción
  202.                 ((RepeatSignal) findViewById(R.id.repeatSignal1)).run(3, 80);
  203.                 SetVolume(Integer.parseInt(RcvMsg[2]));
  204.                 SetMute(Integer.parseInt(RcvMsg[3]) == 1);
  205.                 SetLocked(Integer.parseInt(RcvMsg[4]) == 1);
  206.               }
  207.             }
  208.           } catch (Exception e) {
  209.             // Log.e("LockPCSound", "Timeout Receiver excedido", e);
  210.           }
  211.         }
  212.         socket.close();
  213.       } catch (Exception e) {
  214.         Log.e("LockPCSound", "No se puede crear socket", e);
  215.       }
  216.     }
  217.   }
  218.  
  219.   protected void ShowToast(final String Msg) {
  220.     Toast toast = Toast.makeText(getApplicationContext(), Msg,
  221.         Toast.LENGTH_SHORT);
  222.     toast.show();
  223.   }
  224.  
  225.   public void showToast(final String toast) {
  226.     runOnUiThread(new Runnable() {
  227.       public void run() {
  228.         Toast.makeText(MainActivity.this, toast, Toast.LENGTH_SHORT).show();
  229.       }
  230.     });
  231.   }
  232.  
  233.   protected void TurnOnWifi() {
  234.     WifiManager wifiManager = (WifiManager) this
  235.         .getSystemService(Context.WIFI_SERVICE);
  236.     if (!wifiManager.isWifiEnabled()) {
  237.       wifiManager.setWifiEnabled(true);
  238.       ShowToast("Wifi desconectada. Conectando...");
  239.     }
  240.   }
  241.  
  242.   protected void invalidateView(final View view) {
  243.     Message message = new Message();
  244.     message.what = MSG_INVALIDATE;
  245.     message.obj = view;
  246.     handler.sendMessage(message);
  247.   }
  248.  
  249.   // Envio de mensajes para el cambio de controles modo threadsafe
  250.   protected synchronized void SetLocked(final boolean v) {
  251.     Message message = new Message();
  252.     message.what = MSG_LOCKVALUE;
  253.     message.obj = LockCheck;
  254.     message.arg1 = v ? 1 : 0;
  255.     handler.sendMessage(message);
  256.   }
  257.  
  258.   protected synchronized void SetMute(final boolean v) {
  259.     Message message = new Message();
  260.     message.what = MSG_MUTEVALUE;
  261.     message.obj = MuteCheck;
  262.     message.arg1 = v ? 1 : 0;
  263.     handler.sendMessage(message);
  264.   }
  265.  
  266.   protected synchronized void SetVolume(final int v) {
  267.     Message message = new Message();
  268.     message.what = MSG_VOLUMEVALUE;
  269.     message.obj = VolumeBar;
  270.     message.arg1 = v / 0x492;
  271.     handler.sendMessage(message);
  272.   }
  273.  
  274.   protected void ShowKeyboard(boolean Show) {
  275.     if (Show)
  276.       ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE))
  277.           .showSoftInput(TextBox, InputMethodManager.SHOW_FORCED);
  278.     else
  279.       ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE))
  280.           .hideSoftInputFromWindow(TextBox.getWindowToken(), 0);
  281.     TextBox.setCursorVisible(Show);
  282.   }
  283.  
  284.   String getVersion(){
  285.     String version = new String("");
  286.     try {
  287.       version = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
  288.     } catch (Exception e) {}
  289.     return version;
  290.   }
  291.  
  292.   protected void SaveData() {
  293.     try {
  294.       FileOutputStream file = openFileOutput("data", Context.MODE_PRIVATE);
  295.       file.write((getVersion() + "\n" + UDPid).getBytes());
  296.       file.close();
  297.     } catch (IOException e) {
  298.       Log.e("Controller",
  299.           e.getMessage() + e.getLocalizedMessage() + e.getCause());
  300.     }
  301.   }
  302.  
  303.   // Datos persistentes
  304.   protected void LoadData() {
  305.     try {
  306.       BufferedReader input = new BufferedReader(new InputStreamReader(
  307.           openFileInput("data")));
  308.       String version = input.readLine().toString();
  309.       UDPid = input.readLine().toString();
  310.       ((EditText) findViewById(R.id.editText)).setText(UDPid);
  311.       if(!getVersion().equals(version)){
  312.         showAbout();
  313.       }
  314.       input.close();
  315.     } catch (IOException e) {
  316.       Log.e("Controller",
  317.           e.getMessage() + e.getLocalizedMessage() + e.getCause());
  318.     }
  319.   }
  320.  
  321.   @Override
  322.   protected void onCreate(Bundle savedInstanceState) {
  323.     super.onCreate(savedInstanceState);
  324.     setContentView(R.layout.main_frame);
  325.  
  326.     EnableHandler = true;
  327.     EnableUpdateVolume = true;
  328.     receiver = new Receiver();
  329.     new Thread(receiver).start();
  330.     LoadData();
  331.     MainFrame = findViewById(R.layout.main_frame);
  332.     MainContent = findViewById(R.id.Main_Content);
  333.     MainContent.setOnClickListener(new View.OnClickListener() {
  334.       @Override
  335.       public void onClick(View view) {
  336.         // Petición de actualizar status
  337.         Send("Get", 0);
  338.         ShowKeyboard(false);
  339.       }
  340.     });
  341.  
  342.     TextBox = (EditText) findViewById(R.id.editText);
  343.  
  344.     TextBox.setOnClickListener(new View.OnClickListener() {
  345.       @Override
  346.       public void onClick(View view) {
  347.         ShowKeyboard(true);
  348.       }
  349.     });
  350.  
  351.     TextBox.addTextChangedListener(new TextWatcher() {
  352.       public void afterTextChanged(Editable s) {
  353.         UDPid = ((EditText) findViewById(R.id.editText)).getText().toString();
  354.         if (UDPid.equals("Broadcast") || UDPid.equals("255.255.255.255"))
  355.           Address = null;
  356.         else
  357.           //GetAddr();
  358.           Send("Get", 0);
  359.       }
  360.  
  361.       public void beforeTextChanged(CharSequence s, int start, int count,
  362.           int after) {
  363.         Send("Get", 0);
  364.     }
  365.  
  366.       public void onTextChanged(CharSequence s, int start, int before, int count) {
  367.       }
  368.     });
  369.  
  370.     TextBox.setOnKeyListener(new OnKeyListener() {
  371.       @Override
  372.       public boolean onKey(View v, int keyCode, KeyEvent event) {
  373.         if (keyCode == 66) {
  374.           ShowKeyboard(false);
  375.           // Petición de actualizar status
  376.           Send("Get", 0);
  377.           return true;
  378.         }
  379.         return false;
  380.       }
  381.     });
  382.  
  383.     VolumeBar = (SeekBar) findViewById(R.id.volumebar);
  384.     VolumeBar.setMax(0x38);
  385.     VolumeBar.setProgress(0);
  386.     VolumeBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
  387.       @Override
  388.       public void onProgressChanged(SeekBar seekBar, int progress,
  389.           boolean fromUser) {
  390.         if (fromUser)
  391.           Send("Volume", progress * 0x492);
  392.       }
  393.  
  394.       @Override
  395.       public void onStartTrackingTouch(SeekBar seekBar) {
  396.         EnableUpdateVolume = false;
  397.         Send("Get", 2);
  398.         ShowKeyboard(false);
  399.       }
  400.  
  401.       @Override
  402.       public void onStopTrackingTouch(SeekBar seekBar) {
  403.         Send("Get", 2);
  404.         // Activo el EnableHandler con 200 ms de retraso para evitar
  405.         // reverberancia
  406.         TimerTask mTask = new TimerTask() {
  407.           @Override
  408.           public void run() {
  409.             EnableUpdateVolume = true;
  410.           }
  411.         };
  412.         VolumeBar.postDelayed(mTask, 200);
  413.       }
  414.     });
  415.  
  416.     VolumeBar.setOnClickListener(new View.OnClickListener() {
  417.       @Override
  418.       public void onClick(View view) {
  419.         Send("Get", 2);
  420.       }
  421.     });
  422.  
  423.     MuteCheck = (ToggleButton)findViewById(R.id.mutecheck);
  424.     MuteCheck.setOnClickListener(new View.OnClickListener() {
  425.       @Override
  426.       public void onClick(View view) {
  427.         Send("Mute", MuteCheck.isChecked() ? 1 : 0);
  428.         Send("Get", 1);
  429.         ShowKeyboard(false);
  430.       }
  431.     });
  432.  
  433.     LockCheck = (ToggleButton)findViewById(R.id.lockcheck);
  434.     LockCheck.setOnClickListener(new View.OnClickListener() {
  435.       @Override
  436.       public void onClick(View view) {
  437.       // Send("Volume", VolumeBar.getProgress() * 0x492);
  438.         Send("Lock", LockCheck.isChecked() ? 1 : 0);
  439.         Send("Get", 1);
  440.         ShowKeyboard(false);
  441.       }
  442.     });
  443.    
  444.     Send("Get", 0);
  445.   }
  446.  
  447.   @Override
  448.   public boolean onCreateOptionsMenu(Menu menu) {
  449.     // Inflate the menu; this adds items to the action bar if it is present.
  450.     getMenuInflater().inflate(R.menu.main, menu);
  451.     return true;
  452.   }
  453.  
  454.   @Override
  455.   public boolean onOptionsItemSelected(MenuItem item) {
  456.     switch (item.getItemId()) {
  457.     case R.id.about:
  458.       showAbout();
  459.       return true;
  460.     default:
  461.       return super.onOptionsItemSelected(item);
  462.     }
  463.   }
  464.  
  465.   protected void showAbout() {
  466.     // Inflate the about message contents
  467.     View messageView = getLayoutInflater().inflate(R.layout.about, null, false);
  468.  
  469.     TextView textView = (TextView) messageView.findViewById(R.id.app_version);
  470.     textView.append("Version " + getVersion() + "\n");
  471.     int defaultColor = textView.getTextColors().getDefaultColor();
  472.     textView.setTextColor(defaultColor);
  473.  
  474.     textView = (TextView) messageView.findViewById(R.id.about_credits);
  475.     defaultColor = textView.getTextColors().getDefaultColor();
  476.     textView.setTextColor(defaultColor);
  477.  
  478.     AlertDialog.Builder builder = new AlertDialog.Builder(this);
  479.     builder.setIcon(R.drawable.ic_app);
  480.     builder.setTitle(R.string.app_name);
  481.     builder.setView(messageView);
  482.     builder.create();
  483.     builder.show();
  484.   }
  485.  
  486.   // Optimizando recursos android
  487.   @Override
  488.   public void onResume() {
  489.     super.onResume();
  490.     receiver.Resume();
  491.   }
  492.  
  493.   @Override
  494.   public void onPause() {
  495.     super.onPause();
  496.     SaveData();
  497.     receiver.Pause();
  498.  
  499.     if (isFinishing()){
  500.       Send("GoogBye", 0);
  501.       receiver.Terminate();
  502.     }   
  503.   }
  504.  
  505. }

El layout es el siguiente:

delphi
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="match_parent" >
  5.  
  6.     <ImageView
  7.         android:id="@+id/imageView1"
  8.         android:layout_width="fill_parent"
  9.         android:layout_height="fill_parent"
  10.         android:scaleType="centerCrop"
  11.         android:src="@drawable/ic_fondo" />
  12.  
  13.     <RelativeLayout
  14.         android:id="@+id/Main_Content"
  15.         android:layout_width="match_parent"
  16.         android:layout_height="match_parent"
  17.         android:paddingLeft="13dp"
  18.         android:paddingRight="13dp" >
  19.  
  20.         <LinearLayout
  21.             android:id="@+id/linearLayout1"
  22.             android:layout_width="match_parent"
  23.             android:layout_height="wrap_content"
  24.             android:layout_marginTop="50dp"
  25.             android:gravity="center"
  26.             android:orientation="vertical" >
  27.            
  28.             <TextView
  29.                 android:id="@+id/textView0"
  30.                 android:layout_width="wrap_content"
  31.                 android:layout_height="wrap_content"
  32.                 android:layout_marginTop="20dp"
  33.                 android:text="@string/Identificacion"
  34.                 android:textAppearance="?android:attr/textAppearanceSmall"
  35.                 android:textColor="#ffffff" />
  36.  
  37.             <LinearLayout
  38.                 android:layout_width="match_parent"
  39.                 android:layout_height="wrap_content"
  40.                 android:layout_marginTop="10dp"
  41.                 android:gravity="center"
  42.                 android:orientation="horizontal" >
  43.  
  44.                 <EditText
  45.                     android:id="@+id/editText"
  46.                     android:layout_width="match_parent"
  47.                     android:layout_height="match_parent"
  48.                     android:layout_weight="1"
  49.                     android:background="@drawable/edit"
  50.                     android:inputType="text"
  51.                     android:paddingRight="10dp"
  52.                     android:shadowColor="@android:color/background_dark"
  53.                     android:text="@string/uno"
  54.                     android:textColor="#000000" >
  55.  
  56.                     <requestFocus />
  57.                 </EditText>
  58.  
  59.                 <com.escafandra.lockpcsound.RepeatSignal
  60.                     android:id="@+id/repeatSignal1"
  61.                     android:layout_width="match_parent"
  62.                     android:layout_height="match_parent"
  63.                     android:layout_weight="6"
  64.                     android:paddingLeft="10dp"
  65.                     android:background="#00000000"
  66.                     android:scaleType="center"
  67.                     android:src="@drawable/led_selector" />
  68.             </LinearLayout>
  69.  
  70.             <LinearLayout
  71.                 android:layout_width="match_parent"
  72.                 android:layout_height="wrap_content"
  73.                 android:layout_marginTop="25dp"
  74.                 android:gravity="center"
  75.                 android:orientation="horizontal" >
  76.  
  77.                 <SeekBar
  78.                     android:id="@+id/volumebar"
  79.                     android:layout_width="wrap_content"
  80.                     android:layout_height="wrap_content"
  81.                     android:layout_weight="2"
  82.                     android:focusable="true"
  83.                     android:indeterminate="false"
  84.                     android:max="65535"
  85.                     android:paddingRight="10dp"
  86.                     android:progress="0"
  87.                     android:thumb="@drawable/thumb"
  88.                     android:thumbOffset="0dp" />
  89.  
  90.                 <ToggleButton
  91.                     android:id="@+id/mutecheck"
  92.                     android:layout_width="48dp"
  93.                     android:layout_height="wrap_content"
  94.                     android:background="@null"
  95.                     android:button="@drawable/selector_mute"
  96.                     android:gravity="center"
  97.                     android:paddingLeft="5dp"
  98.                     android:textOff=" "
  99.                     android:textOn=" " />
  100.             </LinearLayout>
  101.            
  102.             <TextView
  103.                 android:id="@+id/textView1"
  104.                 android:layout_width="wrap_content"
  105.                 android:layout_height="wrap_content"
  106.                 android:layout_marginTop="0dp"
  107.                 android:text="@string/Volumen"
  108.                 android:textAppearance="?android:attr/textAppearanceSmall"
  109.                 android:textColor="#ffffff" />
  110.  
  111.             <LinearLayout
  112.                 android:layout_width="match_parent"
  113.                 android:layout_height="wrap_content"
  114.                 android:layout_marginTop="55dp"
  115.                 android:gravity="center"
  116.                 android:orientation="horizontal" >
  117.  
  118.                 <ToggleButton
  119.                     android:id="@+id/lockcheck"
  120.                     android:layout_width="52dp"
  121.                     android:layout_height="wrap_content"
  122.                     android:background="@null"
  123.                     android:button="@drawable/selector_lock"
  124.                     android:gravity="center"
  125.                     android:paddingLeft="5dp"
  126.                     android:textOff=" "
  127.                     android:textOn=" " />
  128.             </LinearLayout>
  129.          
  130.            
  131.         </LinearLayout>
  132.  
  133.     </RelativeLayout>
  134.        
  135. </FrameLayout>

El archivo manifest para esta APP debe ser como sigue:

delphi
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3.     package="com.escafandra.lockpcsound"
  4.     android:versionCode="0100"
  5.     android:versionName="0.1.0.0" >
  6.  
  7.     <uses-sdk
  8.         android:minSdkVersion="8"
  9.         android:targetSdkVersion="17" />
  10.    
  11.     <uses-permission android:name="android.permission.INTERNET" />
  12.     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
  13.     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
  14.  
  15.     <application
  16.         android:allowBackup="true"
  17.         android:icon="@drawable/ic_app"
  18.         android:label="@string/app_name"
  19.         android:theme="@style/AppTheme" >
  20.         <activity
  21.             android:name="com.escafandra.lockpcsound.MainActivity"
  22.             android:label="@string/app_name"
  23.             android:screenOrientation="portrait">
  24.             <intent-filter>
  25.                 <action android:name="android.intent.action.MAIN" />
  26.  
  27.                 <category android:name="android.intent.category.LAUNCHER" />
  28.             </intent-filter>
  29.         </activity>
  30.     </application>
  31.  
  32. </manifest>

La imagen de fondo de esta app contiene un «huevo de pascua» a ver quién lo descubre:

5hvo.jpg


Publico adjuntos fuentes y binarios.

Espero que esta actualización sea de utilidad.


Saludos.
  • 0

#58 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.446 mensajes
  • LocationMéxico

Escrito 10 abril 2014 - 11:39

Vaya, no puedo ver la imagen, está bloqueada por el maldito firewall de la oficina. Me quedaré con las ganas de participar  8o|

Saludos
  • 0

#59 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 10 abril 2014 - 04:17

Gracias maestro escafandra por compartir sus tan interesantes trabajos. (y)
  • 0

#60 seoane

seoane

    Advanced Member

  • Administrador
  • 1.259 mensajes
  • LocationEspaña

Escrito 10 abril 2014 - 04:25

:ap: :ap: :ap: Esto hay que verlo con calma ...
  • 0




IP.Board spam blocked by CleanTalk.