Skip to content

Android 手电筒小程序

ythy edited this page May 10, 2018 · 2 revisions

手电筒小程序涉及2方面内容:

  1. home widget
  2. camera

开发步骤

  1. 需要增加权限,用于手电筒
    <!-- 打开Camera的权限 -->
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.autofocus" />

    <!-- 开启闪光灯权限 -->
    <uses-permission android:name="android.permission.FLASHLIGHT" />
  1. 桌面小程序 增加 receiver配置
        <receiver
            android:name="com.mx.easytouch.receiver.TorchWidgetProvider"  //更新小程序文件 继承 AppWidgetProvider
            android:icon="@drawable/flash_on"          //小程序一览中的图标
            android:label="@string/type_torch" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />  // 固定代码
            </intent-filter>
            <intent-filter>
                <action android:name="COM_FLASHLIGHT" >  // 自定义action 用于手电筒状态切换
                </action>
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"     //固定代码
                android:resource="@xml/torch_appwidget_info" /> //小程序布局配置文件
        </receiver>
  1. 桌面小程序布局 分2部分
//第一部分 @xml/torch_appwidget_info

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minHeight="60dp"
    android:minWidth="60dp"
    android:updatePeriodMillis="0" //不自动刷新
    android:widgetCategory="home_screen|keyguard" //支持主屏和锁屏(高版本支持) 
    android:initialLayout="@layout/widget_torch">
</appwidget-provider>

//第二部分 @layout/widget_torch

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center_horizontal">

    <ImageButton
        android:id="@+id/btnTorch"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@null"/>
</LinearLayout>
  1. 基于 AppWidgetProvider 操作小程序。 注意:更新小程序样式,必须通过 onUpdate()生命周期函数进行
    onReceive() 函数是此页面所有函数执行的基础,必须调用 super.onReceive(context, intent); 否则生命周期函数不再触发
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager,
                         int[] appWidgetIds) {
        Intent receiver = new Intent(context, TorchWidgetProvider.class);
        receiver.setAction(Intent.ACTION_VIEW);
        receiver.setAction(RECEIVE_FLASH);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, receiver, 0); // 广播形式派发intent

        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_torch); 
        views.setOnClickPendingIntent(R.id.btnTorch, pendingIntent); // 点击按钮切换闪光灯状态
        if(isLightOn) {
            views.setImageViewResource(R.id.btnTorch, R.drawable.flash_on);
            startTorchService(context, true);
        } else {
            views.setImageViewResource(R.id.btnTorch, R.drawable.flash_off);
            startTorchService(context, false);
        }
        appWidgetManager.updateAppWidget(appWidgetIds, views);
    }
    
    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        if(RECEIVE_FLASH.equals(intent.getAction())){  //此处调用 appWidgetManager.updateAppWidget 无效
            handleCamera(context);
            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context.getApplicationContext());
            ComponentName thisWidget = new ComponentName(context.getApplicationContext(), TorchWidgetProvider.class);
            int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
            if (appWidgetIds != null && appWidgetIds.length > 0) {
                onUpdate(context, appWidgetManager, appWidgetIds);
            }
        }
    }
    
  1. 打开闪光灯。 camera 方式在Android 6以后被废弃,需要判断版本调用不同API。 注意: 此处调用基于反射,高版本API不能直接
    在此处调用,可以通过其他Class封装调用, 模拟器测试Android API 16报错
   //省略调用部分
   
   private void handleCamera(Context context){
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            handleCameraOld();
        } else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            handleCameraNew(context);
        }
    }
    
    @RequiresApi(api = Build.VERSION_CODES.M)
    private void handleCameraNew(Context context){
        boolean result = Camera2Utils.torchSwitch(context, isLightOn); //通过公共方法调用API
        if(result)
            isLightOn = !isLightOn;
    }
    
    代码如下
    CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
    manager.setTorchMode("0", isLightOn ? false: true);
  1. 打开闪光灯时,通知栏显示, 点击通知栏,调用小程序手电状态切换的receiver。 此处增加 TorchService
    注意: pendingIntent 必须通过PendingIntent.getBroadcast() 派发broadcast, 否则receiver 接收不到
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        startTorchNotification();
        return START_NOT_STICKY;
    }

    private void startTorchNotification(){
        Intent notificationIntent = new Intent(this, TorchWidgetProvider.class);
        notificationIntent.setAction(Intent.ACTION_VIEW);
        notificationIntent.setAction(TorchWidgetProvider.RECEIVE_FLASH);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, notificationIntent, 0); 

        this.torchNotification = new Notification.Builder(getApplicationContext())
                .setContentTitle("Torch turned on")
                .setContentText("Tap to turn off.")
                .setContentIntent(pendingIntent)
                .setSmallIcon(R.drawable.won)
                .build();
        torchNotification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
        startForeground(NOTIFICATION_ID, torchNotification);
    }

后续

  1. 相机操作可以转移到service中进行
  2. 小程序的外观调整(和桌面快捷方式不一致)
Clone this wiki locally