0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Androidをペンタブレットにする(2/3)

Last updated at Posted at 2020-04-05

前回、Androidをペンタブレットにする(1/3) を投稿しましたが、タッチパネルだけに飽き足らず、Androidスマホが持っているいろんな機能を取り込んでみました。

そのうち、以下の機能の実装を備忘録としておきます。スニペット的に残しておきましたので、コピペで使ってください。

• Androidの地磁気センサー、ジャイロスコープ、加速度センサーを検出して、Windowsに通知する。
• AndroidのGPS情報を取得し、Windowsに通知する。
• AndroidでQRコードをスキャンし、Windowsに通知する。
• Androidで音声認識して、Windowsに通知する。
• AndroidのクリップボードにWindowsからコピーやペーストする。

地磁気センサー、ジャイロスコープ、加速度センサー

〇利用するクラス宣言

import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;

〇変数定義

  SensorManager sensorManager;
  boolean isGyroscope = false;
  boolean isAccelerometer = false;
  boolean isMagnetic = false;

〇onCreateにて

    sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);

センサー機能を有効、無効にします。
isMagnetic等は、アクティビティがアクティブでない場合に一時停止し、アクティブになったら再度開始にするためのものです。

    private void setSensorEnable(int type, boolean enable){
        if( type == Sensor.TYPE_MAGNETIC_FIELD ) {
            isMagnetic = enable;
        }else if( type == Sensor.TYPE_GYROSCOPE){
            isGyroscope = enable;
        }else if( type == Sensor.TYPE_ACCELEROMETER ){
            isAccelerometer = enable;
        }
        if (enable)
            sensorStart(type);
        else
            sensorStop(type);
    }

アクティビティがアクティブでないときは、センサー検知を一時停止にします。

    @Override
    protected void onResume(){
        super.onResume();

        if(isGyroscope)
            sensorStart(Sensor.TYPE_GYROSCOPE);
        if( isAccelerometer )
            sensorStart(Sensor.TYPE_ACCELEROMETER);
        if( isMagnetic )
            sensorStart(Sensor.TYPE_MAGNETIC_FIELD);
    }

    @Override
    protected void onPause(){
        super.onPause();

        sensorStop(Sensor.TYPE_GYROSCOPE);
        sensorStop(Sensor.TYPE_ACCELEROMETER);
        sensorStop(Sensor.TYPE_MAGNETIC_FIELD);
    }

以下が、センサー検知イベントの開始です。
registerListenerで登録したクラスにイベントが通知されます。SensorEventListenerクラスを実装する必要があります。イベントの頻度は、SENSOR_DELAY_NORMAL以外にもいくつか種類があります。

    private void sensorStart(int type){
        if( sensorManager == null )
            return;

        if( type == Sensor.TYPE_MAGNETIC_FIELD ) {
            Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
            if (sensor != null) {
                sensorManager.registerListener(sensorListnerMagnetic, sensor, SensorManager.SENSOR_DELAY_NORMAL);
            }
        }else if( type == Sensor.TYPE_GYROSCOPE ){
            Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
            if (sensor != null) {
                sensorManager.registerListener( sensorListnerGyro,  sensor, SensorManager.SENSOR_DELAY_NORMAL);
            }
        }else if( type == Sensor.TYPE_ACCELEROMETER){
            Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
            if (sensor != null) {
                sensorManager.registerListener( sensorListnerAccel,  sensor, SensorManager.SENSOR_DELAY_NORMAL);
            }
        }
    }

以下が、センサー検知イベントの停止です。

    private void sensorStop(int type){
        if( sensorManager == null )
            return;

        if( type == Sensor.TYPE_MAGNETIC_FIELD ) {
            sensorManager.unregisterListener(sensorListnerMagnetic);
        }else if( type == Sensor.TYPE_GYROSCOPE ){
            sensorManager.unregisterListener(sensorListnerGyro);
        }else if( type == Sensor.TYPE_ACCELEROMETER){
            sensorManager.unregisterListener(sensorListnerAccel);
        }
    }

以降、センサー検知イベントの受信処理のためのクラス実装です。地磁気センサー、ジャイロスコープ、加速度センサーとありますが、同じフォーマットです。

    private SensorEventListener sensorListnerMagnetic = new SensorEventListener(){
        @Override
        public void onSensorChanged(SensorEvent sensorEvent) {
            switch( sensorEvent.sensor.getType() ){
                case Sensor.TYPE_MAGNETIC_FIELD:{
// BLEセントラルにイベントを転送
                    int unit = Float.SIZE / Byte.SIZE;
                    byte[] send_buffer = new byte[1 + 3 * unit];
                    send_buffer[0] = Const.RSP_MAGNETIC;
                    setFloatBytes(send_buffer, 1, sensorEvent.values[0]);
                    setFloatBytes(send_buffer, 1 + unit, sensorEvent.values[1]);
                    setFloatBytes(send_buffer, 1 + 2 * unit, sensorEvent.values[2]);
                    sendBuffer(send_buffer, send_buffer.length);                    break;
                }
            }
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int i) {
            // do nothieng
        }
    };

    private SensorEventListener sensorListnerGyro = new SensorEventListener(){
        @Override
        public void onSensorChanged(SensorEvent sensorEvent) {
            switch( sensorEvent.sensor.getType() ){
                case Sensor.TYPE_GYROSCOPE:{
// BLEセントラルにイベントを転送
                    break;
                }
            }
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int i) {
            // do nothieng
        }
    };

    private SensorEventListener sensorListnerAccel = new SensorEventListener(){
        @Override
        public void onSensorChanged(SensorEvent sensorEvent) {
            switch( sensorEvent.sensor.getType() ){
                case Sensor.TYPE_ACCELEROMETER:{
// BLEセントラルにイベントを転送
                    break;
                }
            }
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int i) {
            // do nothieng
        }
    };

#GPS情報

appのbuild.gradleのdependenciesに以下を追加します。

build.gradle
    implementation 'com.google.android.gms:play-services-location:17.0.0'

AndroidManifest.xmlに以下を追加します。

AndroidManifest.xml
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

〇利用するクラス宣言

import android.location.Location;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;

〇変数宣言

    FusedLocationProviderClient fusedLocationClient;

〇onCreateにて

        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
        LocationRequest locationRequest = new LocationRequest();
        locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

GPS情報取得には、ユーザに対して許可を得る必要があります。許可を得ていないのであれば、許可を要求し、すでに得ているのであれば、GPS情報を取得します。

    private void getLocation() {
        if (fusedLocationClient == null)
            return;

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_PERMISSION_LOCATION);
        }else {
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                fusedLocationClient.getLastLocation().addOnCompleteListener(this, new OnCompleteListener<Location>() {
                    @Override
                    public void onComplete(@NonNull Task<Location> task) {
                        Location location = task.getResult();
                        sendLocation(location.getLatitude(), location.getLongitude());
                    }
                });
            }
        }
    }

GPS情報取得の許可をユーザから得ると、以下のActivityの関数が呼ばれます。許可を得たのち、GPSを取得します。

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults){
        if( requestCode == REQUEST_PERMISSION_LOCATION )
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED)
                getLocation();
    }

QRコードスキャン

こちらを使わせていただきました。

journeyapps/zxing-android-embedded
 https://github.com/journeyapps/zxing-android-embedded

appのbuild.gradleのdependenciesに以下を追加します。

build.gradle
    implementation 'com.journeyapps:zxing-android-embedded:3.2.0@aar'
    implementation 'com.google.zxing:core:3.2.1'

〇利用するクラス宣言

import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;

あとは、以下を呼ぶだけです。カメラが起動し、QRコードをスキャンします。

    private void startQrScan(){
        new IntentIntegrator(this).initiateScan();
    }

スキャンが完了すると、以下のActivityの関数が呼ばれます。result.getContents()にスキャンした文字列があります。

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
        if(result != null) {
            if(result.getContents() != null)
                sendQrcode(result.getContents());
            return;
        }

        super.onActivityResult(requestCode, resultCode, data);

// ・・・

音声認識

AndroidManifest.xmlに以下を追加します。

AndroidManifest.xml
    <uses-permission android:name="android.permission.INTERNET" />

〇利用するクラス宣言

import android.speech.RecognizerIntent;

あとは、以下を呼ぶだけです。

    private void doRecognize(){
        Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.JAPAN.toString() );
        intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 100);
        intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "音声を入力");
        startActivityForResult(intent, REQUEST_RECOGNITION);
    }

音声認識が完了すると、Activityの以下の関数が呼ばれます。candidates.get(0)に一番可能性の高い文章が入っています。

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

//・・・

        super.onActivityResult(requestCode, resultCode, data);
        if(requestCode == REQUEST_RECOGNITION && resultCode == RESULT_OK) {
            ArrayList<String> candidates = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
            if( candidates.size() > 0 )
                sendText(Const.TYPE_TEXT_RECOGNITION, candidates.get(0) );
        }

#クリップボード

〇利用するクラス宣言

import android.content.ClipData;
import android.content.ClipboardManager;

〇変数宣言

    ClipboardManager clipboardManager;

〇onCreateにて

        clipboardManager = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);

クリップボードから文字列を取得し、BLEセントラルに転送します。クリップボード操作には直接は関係しませんが、クリップボードから取得したことをToastでもわかるようにしました。

    private void sendCopy(){
        if( clipboardManager == null )
            return;

        ClipData cd = clipboardManager.getPrimaryClip();
        if(cd != null){
            ClipData.Item item = cd.getItemAt(0);
            Toast.makeText(this, item.getText().toString(), Toast.LENGTH_LONG).show();
            sendText(Const.TYPE_TEXT_COPY, item.getText().toString());
        }
    }

BLEセントラルから受け取った文字列をクリップボードにコピーします。
クリップボード操作には直接は関係しませんが、クリップボードに受け取ったことをToast で表示したいのですが、UIスレッドでない場合に備えて、UIスレッドにメッセージ送信しています。

    private void setPaste(String text){
        if( clipboardManager == null )
            return;

        clipboardManager.setPrimaryClip(ClipData.newPlainText("", text));
        handler.sendUIMessage(UIHandler.MSG_ID_TEXT, UIMSG_TOAST, "Receive Clipboard");
    }

以上

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?