Je développe un widget pour allumer/éteindre la caméra led du téléphone.
J'ai créé un widget qui peut fonctionner comme un bouton à bascule (marche/arrêt).
Le comportement est le suivant: Parfois, la lumière LED reste allumée lorsque j'active le widget. Mais cela ne permet pas d'allumer/d'éteindre la LED de la caméra mais cela change l'icône.
Je ne suis pas en mesure de comprendre quel est le problème réel.
La même chose fonctionne très bien dans l'activité (Torch Light Application).
Quelqu'un peut-il m'expliquer comment résoudre mon problème?
Où je vais mal?
Vous pouvez regarder le code ci-dessous que j'ai fait jusqu'à présent
onUpdate
méthode
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
//super.onUpdate(context, appWidgetManager, appWidgetIds);
remoteViews = new RemoteViews( context.getPackageName(), R.layout.widgetlayout);
watchWidget = new ComponentName( context, FlashLightWidget.class );
Intent intentClick = new Intent(context,FlashLightWidget.class);
intentClick.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, ""+appWidgetIds[0]);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, appWidgetIds[0],intentClick, 0);
remoteViews.setOnClickPendingIntent(R.id.myToggleWidget, pendingIntent);
appWidgetManager.updateAppWidget( watchWidget, remoteViews );
ctx=context;
}
La méthode onReceive
est la suivante:
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
remoteViews = new RemoteViews( context.getPackageName(), R.layout.widgetlayout);
if (intent.getAction()==null) {
Bundle extras = intent.getExtras();
if(extras!=null) {
if(status)
{
status=false;
remoteViews.setImageViewResource(R.id.myToggleWidget, R.drawable.shutdown1);
processOnClick();
Toast.makeText(context,"Status==false-onclick",Toast.LENGTH_SHORT).show();
}
else
{
status = true;
remoteViews.setImageViewResource(R.id.myToggleWidget, R.drawable.shutdown2);
processOffClick();
Toast.makeText(context,"Status==true--Ofclick",Toast.LENGTH_SHORT).show();
}
}
watchWidget = new ComponentName( context, FlashLightWidget.class );
(AppWidgetManager.getInstance(context)).updateAppWidget( watchWidget, remoteViews );
}
}
}
processOffClick
méthode
private void processOffClick() {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
mCamera.release();
mCamera = null;
}
}
processOnClick
méthode
private void processOnClick() {
if(mCamera==null)
{
try {
mCamera = Camera.open();
} catch (Exception e) {
e.printStackTrace();
}
}
if (mCamera != null) {
Parameters params = mCamera.getParameters();
List<String> flashModes = params.getSupportedFlashModes();
if (flashModes == null) {
return;
} else {
params.setFlashMode(Parameters.FLASH_MODE_OFF);
mCamera.setParameters(params);
mCamera.startPreview();
String flashMode = params.getFlashMode();
if (!Parameters.FLASH_MODE_TORCH.equals(flashMode)) {
if (flashModes.contains(Parameters.FLASH_MODE_TORCH)) {
params.setFlashMode(Parameters.FLASH_MODE_TORCH);
mCamera.setParameters(params);
}
}
}
} else if (mCamera == null) {
//Toast.makeText(ctx, "Camera not found", Toast.LENGTH_LONG).show();
return;
}
}
Après un long moment, je me suis libéré pour résoudre ce problème.
Voici ce que j'ai fait.
FlashlightWidgetProvider
classe:
public class FlashlightWidgetProvider extends AppWidgetProvider {
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
Intent receiver = new Intent(context, FlashlightWidgetReceiver.class);
receiver.setAction("COM_FLASHLIGHT");
receiver.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, receiver, 0);
RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.widget_layout);
views.setOnClickPendingIntent(R.id.button, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, views);
}
}
et BroadcastReceiver pour FlashlightWidgetReceiver:
public class FlashlightWidgetReceiver extends BroadcastReceiver {
private static boolean isLightOn = false;
private static Camera camera;
@Override
public void onReceive(Context context, Intent intent) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
if(isLightOn) {
views.setImageViewResource(R.id.button, R.drawable.off);
} else {
views.setImageViewResource(R.id.button, R.drawable.on);
}
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
appWidgetManager.updateAppWidget(new ComponentName(context, FlashlightWidgetProvider.class),
views);
if (isLightOn) {
if (camera != null) {
camera.stopPreview();
camera.release();
camera = null;
isLightOn = false;
}
} else {
// Open the default i.e. the first rear facing camera.
camera = Camera.open();
if(camera == null) {
Toast.makeText(context, R.string.no_camera, Toast.LENGTH_SHORT).show();
} else {
// Set the torch flash mode
Parameters param = camera.getParameters();
param.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
try {
camera.setParameters(param);
camera.startPreview();
isLightOn = true;
} catch (Exception e) {
Toast.makeText(context, R.string.no_flash, Toast.LENGTH_SHORT).show();
}
}
}
}
}
Autorisation requise dans Manifest.xml
fichier :
<uses-permission Android:name="Android.permission.CAMERA"></uses-permission>
Enregistrez également les récepteurs dans Manifest.xml
fichier :
<receiver Android:name=".FlashlightWidgetProvider" Android:icon="@drawable/on" Android:label="@string/app_name">
<intent-filter>
<action Android:name="Android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data Android:name="Android.appwidget.provider"
Android:resource="@xml/flashlight_appwidget_info" />
</receiver>
<receiver Android:name="FlashlightWidgetReceiver">
<intent-filter>
<action Android:name="COM_FLASHLIGHT"></action>
</intent-filter>
</receiver>
Note importante: Ce code fonctionne parfaitement si votre téléphone a FLASH_MODE_TORCH
prise en charge.
J'ai testé dans Samsung Galaxy Ace 2.2.1 & 2.3.3. Le code ne fonctionne pas car ce périphérique n'a pas de FLASH_MODE_TORCH.
Fonctionne bien dans HTC Salsa, Wildfire ..
Si quelqu'un peut tester et publier les résultats ici, ce serait mieux.
La meilleure technique pour gérer les clics à partir d'un RemoteViews
consiste à créer un PendingIntent
qui appelle un service et à exécuter la "trucs" que vous voulez dans le service, y compris toutes les mises à jour RemoteViews
supplémentaires pour votre widget. Vous pouvez envoyer les données pertinentes dans les extras d'intention. Le service appelle stopSelf()
à la fin, donc il s'arrête.
Vous ne pouvez conserver aucun état dans un BroadcastReceiver
; le système les exécute sur n'importe quel thread disponible et ne conserve aucune référence à votre instance après avoir appelé onReceive()
. Votre variable mCamera
n'est pas garantie d'être maintenue entre les appels de votre BroadcastReceiver
.
Si vous avez vraiment besoin de maintenir l'état, vous devez le faire dans le service et ne pas utiliser stopSelf()
(jusqu'à un moment approprié).
Vous n'avez pas besoin d'un thread d'interface utilisateur pour utiliser la classe Camera
, sauf si vous effectuez un aperçu d'image, qui nécessite un SurfaceHolder
(et implique une interface utilisateur). Vous devez cependant avoir une boucle d'événements active, sinon Camera
ne vous enverra pas de rappels, ce qui est un problème car Camera
est principalement asynchrone. Vous pouvez le faire dans un service (voir HandlerThread
) et maintenir votre service en marche jusqu'à ce qu'il soit temps de release()
tout. Quel que soit le thread qui appelle Camera.open()
recevra des rappels.
Est-ce que tout le monde a lu la section sur les widgets d'application? http://developer.Android.com/guide/topics/appwidgets/index.html
L'utilisation de la section AppWidgetProvider Class dit à peu près ce que je dis ici.
J'ai eu une situation similaire où je dois exécuter certains Android sur le thread d'interface utilisateur ... qui n'est disponible que dans une activité. Ma solution - une activité avec une mise en page complètement transparente. Donc vous il suffit de voir votre écran d'accueil (bien qu'il ne réponde pas) pendant que vous terminez vos actions, ce qui dans votre cas devrait être assez rapide.
J'ai une solution qui n'est pas géniale mais qui fonctionne. Demandez au widget d'appeler une activité et, dans l'activité, activez le flash, puis fermez l'activité. La même chose pour l'éteindre. Si cela fonctionne dans l'activité, cette solution fonctionnera. Ce n'est pas élégant mais ça marche. Je l'ai essayé.