SQLの窓

2017年06月06日


Android 6.0 エミュレータで 複数の Runtime Permission の対応を簡潔に吸収するクラス( CheckMyPermission )

商品作る場合では無く、テストで面倒なデバイス側での権限の付与の実装を避けたい場合のクラスを作ってみました。getRequestCount() のチェックをしなければ、権限を付与するまでループするようにする事もできます(許可しない場合の UI テスト等にも使えるかもしれません)

CheckMyPermission クラス
package com.example.lightbox.cameratest;

import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;

import java.util.ArrayList;

public class CheckMyPermission {

    private ArrayList<MyPermission> myPermission_list;
    private MainActivity context;

    public CheckMyPermission(MainActivity context, ArrayList<MyPermission> myPermission_list) {
        this.myPermission_list = myPermission_list;
        this.context = context;
    }

    public boolean checkPermission( int requestCode, int[] grantResults ) {
        boolean result = false;
        for( MyPermission myPermission :  myPermission_list) {
            if ( myPermission.getPermissionId() == requestCode ) {
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                        myPermission.setFlg(true);
                }
            }
        }
        int resultCounter = 0;
        for( MyPermission myPermission :  myPermission_list) {
            if ( !myPermission.getFlg() ) {
                if ( myPermission.getRequestCount() > 0 ) {
                    break;
                }
                ActivityCompat.requestPermissions(context,
                        new String[]{myPermission.getPermissionType()}, myPermission.getPermissionId());
                myPermission.setRequestCount(myPermission.getRequestCount()+1);
                break;
            }
            else {
                resultCounter++;
            }
        }
        if ( resultCounter == myPermission_list.size() ) {
            result = true;
        }

        return result;

    }

    public boolean checkPermission(){
        boolean result = false;
        int initCounter = 0;
        for( MyPermission myPermission :  myPermission_list) {
            if (ActivityCompat.checkSelfPermission(context, myPermission.getPermissionType())== PackageManager.PERMISSION_GRANTED){
                initCounter++;
                myPermission.setFlg(true);
            }
        }
        if ( initCounter == myPermission_list.size() ) {
            result = true;
        }
        else {
            for( MyPermission myPermission :  myPermission_list) {

                if ( !myPermission.getFlg() ) {
                    // 最初に許可されていないパーミッション用のダイアログを表示する
                    ActivityCompat.requestPermissions(context,
                            new String[]{myPermission.getPermissionType()}, myPermission.getPermissionId());
                    myPermission.setRequestCount(myPermission.getRequestCount()+1);

                    // 一つだけ処理する
                    break;
                }

            }
        }

        return result;
    }
}

対象となる Permission を ArrayList にセットとして内部で殆どの処理を実行してもらいます。ArrayList にセットする MyPermission クラスは以下のような情報を保持しています

MyPermission クラス
package com.example.lightbox.cameratest;

public class MyPermission {

    private String permissionType;
    private int permissionId;
    private boolean flg = false;
    private int requestCount = 0;

    public MyPermission(String type, int id) {
        permissionType = type;
        permissionId = id;
    }

    public String getPermissionType() {
        return permissionType;
    }

    public int getPermissionId() {
        return permissionId;
    }

    public boolean getFlg() {
        return flg;
    }

    public void setFlg(boolean flg) {
        this.flg = flg;
    }

    public int getRequestCount() {
        return requestCount;
    }

    public void setRequestCount(int requestCount) {
        this.requestCount = requestCount;
    }
}


主な処理はカメラと撮影した画像の保存ですが、カメラはテストを目的としているので 『非推奨』の 旧API を使用しています。

MainActivity
package com.example.lightbox.cameratest;

import android.Manifest;
import android.content.Intent;
import android.hardware.Camera;
import android.media.MediaScannerConnection;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;

public class MainActivity extends AppCompatActivity {

    private String imagePath;
    private OldCamera camera;
    private CheckMyPermission checkMyPermission;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 必要なパーミッションのリスト
        ArrayList<MyPermission> myPermission_list = new ArrayList<MyPermission>();
        myPermission_list.add(new MyPermission(Manifest.permission.CAMERA,100));
        myPermission_list.add(new MyPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE,101));
        // Runtime Permission 用のクラスのインスタンス
        checkMyPermission = new CheckMyPermission(MainActivity.this,myPermission_list);
        // 全ての必要なパーミッションが既に許可されていた場合
        if ( checkMyPermission.checkPermission() ) {
            cameraSettings();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        // 全ての必要なパーミッションが許可された場合
        if ( checkMyPermission.checkPermission( requestCode,  grantResults )) {
            // onCreate で初期処理できるように、MainActivity をリスタート
            Intent intent = getIntent();
            finish();
            startActivity(intent);
        }
    }

    private void cameraSettings() {

        camera = new OldCamera(MainActivity.this.findViewById(R.id.surfaceView) );

        // *************************************
        // ギャラリーに保存する処理
        // *************************************
        Button galleryButton = (Button) MainActivity.this.findViewById(R.id.button);
        galleryButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i("lightbox", "クリック");

                // 撮影
                camera.getCamera().takePicture(null, null, new Camera.PictureCallback() {
                    @Override
                    public void onPictureTaken(byte[] data, Camera camera) {

                        if (data != null) {

                            // ギャラリー用に内部ストレージにフォルダを作成
                            String imageDir = Environment.getExternalStorageDirectory().getPath() + "/cameratest";
                            File file = new File(imageDir);
                            // ディレクトリ初期作成
                            if (!file.exists()) {
                                if (file.mkdir() == false) {
                                    Log.i("lightbox", "ディレクトリを作成できませんでした");
                                    return;
                                }
                            }

                            // ギャラリー用画像保存パス
                            Calendar cal = Calendar.getInstance();
                            SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd_HHmmss");
                            imagePath = imageDir + "/" + sf.format(cal.getTime()) + ".jpg";

                            FileOutputStream jpg;
                            try {
                                jpg = new FileOutputStream(imagePath);
                                jpg.write(data);
                                jpg.close();

                                // ギャラリーに反映
                                MediaScannerConnection.scanFile(
                                        MainActivity.this,
                                        new String[] { imagePath },
                                        new String[] { "image/jpeg" },
                                        null);

                            }
                            catch (Exception e) {
                                e.printStackTrace();
                            }

                            // カメラ機能再開
                            camera.startPreview();

                        }
                    }
                });

            }
        });
    }

}


OldCamera クラス
package com.example.lightbox.cameratest;

import android.content.Context;
import android.hardware.Camera;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;

import java.io.IOException;
import java.util.List;

public class OldCamera implements SurfaceHolder.Callback {

    // 古いカメラAPI。カメラで何かしたいわけでは無いのでこれで実装
    private Camera camera;
    private SurfaceHolder surfaceHolder;
    private List<Camera.Size> sizeList;

    public OldCamera(View view) {

        Log.i("lightbox", "コンストラクタ");

        // SurfaceView から holder を取り出す
        surfaceHolder = ((SurfaceView)view).getHolder();
        // この中でイベント処理を行う
        surfaceHolder.addCallback(OldCamera.this);

    }

    public Camera getCamera() {

        return camera;

    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {

        Log.i("lightbox", "開く");

        // カメラを開く
        camera = Camera.open();
        try {
            // カメラに holder を渡す
            camera.setPreviewDisplay(holder);
        }
        catch (IOException e) {
            e.printStackTrace();
        }

        // 縦固定(AndroidManifest.xml) で、portrait 撮影を前提
        camera.setDisplayOrientation(90);

        // カメラの情報
        Camera.Parameters params = camera.getParameters();
        // サポートされているサイズの一覧
        sizeList = params.getSupportedPreviewSizes();

    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        // 縦固定(AndroidManifest.xml) なので一度しか通らない
        Log.i("lightbox", "開始");

        // 縦横サイズの設定
        Camera.Parameters params = camera.getParameters();
        Camera.Size optimalSize = getOptimalPreviewSize(sizeList,width,height);
        params.setPreviewSize(optimalSize.width,optimalSize.height);
        camera.setParameters(params);

        camera.startPreview();

    }

    // http://qiita.com/zaburo/items/b5d3815d3ec45b0daf4f
    private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {

        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio=(double)h / w;

        if (sizes == null) return null;

        Camera.Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        for (Camera.Size size : sizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Camera.Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }
        return optimalSize;
    }


    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

        Log.i("lightbox", "終了");

        // プレビュー終了
        if ( camera != null ) {
            camera.stopPreview();
            camera.release();
            camera = null;
        }

    }
}


画面
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.lightbox.cameratest.MainActivity">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Button" />
    </LinearLayout>

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>


AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.lightbox.cameratest">

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


posted by lightbox at 2017-06-06 20:56 | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

Android 6.0 の Runtime Permission に対応する前に、AndroidManifest.xml に権限の記述の必要無いプライベートな書き込みで情報を収集する

Android の中でなかなかテキストファイルに書き込む機会も無いので、FileOutputStream と SharedPreferences でカメラで撮った画像を外部ストレージに書き込むのに知っておきたい情報の確認をしています。

ボタンのイベント

一般的に3種類あるボタンのイベントの作成方法で、ボタンの id によって処理を分けていく結果になる使い方です。ソースとしては自然と連続になるので、このような場合( Write と Read ) では第三者から見たら解りやすくなると思います。(MainActivity に implements View.OnClickListener が必要ですね)

※ 他の二つは、無名(匿名)のインナーでその場に書いてしまう方法と、ボタンのプロパティにメソッド名登録して、MainActivity の メソッドとしてイベントする方法です。


書き込み処理では、BufferedWriter はほぼ無駄なので使っていません。最後に改行コードつけておけば、BufferedReader で読めますし。

書き込んだ後は、Android Device Monitor で push してリアルに確認するのがいいですね。Android に騙されないで、なんか安心します。



test.txt

※ test.txt は、files フォルダのの下にあります
ストレージ情報
/storage/emulated/0
android.permission.CAMERA
android.permission.WRITE_EXTERNAL_STORAGE
0

Environment.getExternalStorageDirectory().getPath()/storage/emulated/0 が取得できています。ここに書き込みするためには Android 6.0 では Runtime Permission の処理が必要になってくるわけですが、今は場所のみを Android Device Monitor で確認しておきます。
package com.example.lightbox.textfiletest;

import android.Manifest;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.Calendar;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.i("lightbox", "implements View.OnClickListener したら、setOnClickListener(this)");
        findViewById(R.id.button).setOnClickListener(this);
        findViewById(R.id.buttonRead).setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {

        // View から ID を取得して仕分け
        if (v.getId() == R.id.button ) {

            Log.i("lightbox", "AndroidManifest.xml に権限の記述の必要無いプライベートな書き込み");
            try {
                FileOutputStream fileOutputStream = openFileOutput("test.txt", MODE_PRIVATE);
                OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "utf-8");

                // 次の段階のストレージアクセスの為の情報集め
                outputStreamWriter.write("ストレージ情報\n");
                outputStreamWriter.write(String.format("%s\n", Environment.getExternalStorageDirectory().getPath()));
                // String
                outputStreamWriter.write(Manifest.permission.CAMERA + "\n");
                outputStreamWriter.write(Manifest.permission.WRITE_EXTERNAL_STORAGE + "\n");
                // int
                outputStreamWriter.write(PackageManager.PERMISSION_GRANTED + "\n");

                outputStreamWriter.close();
                fileOutputStream.close();

                // プライベートな小さな情報を保存する為の本来の方法
                SharedPreferences data = getSharedPreferences("private_data", MODE_PRIVATE);
                SharedPreferences.Editor editor = data.edit();

                // 現在の年月日・時分秒を取得する為の準備
                Calendar cal = Calendar.getInstance();
                SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd_HHmmss");

                editor.putString("now", sf.format(cal.getTime()));
                editor.commit();

            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
        else if ( v.getId() == R.id.buttonRead ) {

            Log.i("lightbox", "読み込み");
            try {
                FileInputStream fileINputStream = openFileInput("test.txt");
                InputStreamReader inputStreamReader = new InputStreamReader(fileINputStream, "utf-8");
                BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

                String line_buffer;
                while( null != (line_buffer = bufferedReader.readLine()) ) {
                    // 長さ 0 で Empty
                    if (line_buffer.isEmpty()) {
                        continue;
                    }

                    Log.i("lightbox",line_buffer);

                }

                inputStreamReader.close();
                fileINputStream.close();

                SharedPreferences data = getSharedPreferences("private_data", MODE_PRIVATE);
                Log.i("lightbox", data.getString("now", "このデータはありません"));

            }
            catch (Exception e) {
                e.printStackTrace();
            }

        }

    }
}

Manifest.permission.CAMERA や Manifest.permission.WRITE_EXTERNAL_STORAGE は、Runtime Permission のチェックで使う定数です。これを ActivityCompat.checkSelfPermission に渡して利用可能の有無をまずチェックします。(実際は継承元の ContextCompat のメソッド)



posted by lightbox at 2017-06-06 11:23 | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

2017年05月28日


Android Studio : エミュレータで Notification(通知)のテスト



こんな感じで結果的には表示されます。単なる動作確認のテストなので、MainActivity でボタンをクリックしたイベント内で呼び出しています。呼び出す前のステータスバーは、以下のようになります。



ボタンをクリックすると、以下のようになります。



このステータスバー(通知領域)から、下にドラッグ(エミュレータなので)すると通知画面が開きます。開いた通知をクリックすると、設定しておいた Google のドキュメントページをブラウザで開きます


Develop > API Guides > 通知

   public void notification(View view) {

        Log.i("lightbox", "notification");
        Notification.Builder builder = new Notification.Builder(getApplicationContext());

        // 通知内容の作成
        builder.setSmallIcon(android.R.drawable.ic_dialog_info);
        builder.setContentTitle("通知テスト");
        builder.setContentText("通知内容");
        builder.setSubText("説明");
        builder.setContentInfo("右下");
        builder.setPriority(Notification.PRIORITY_DEFAULT);
        builder.setAutoCancel(true);

        // 通知から呼び出される処理の作成
        Uri uri = Uri.parse("https://developer.android.com/guide/topics/ui/notifiers/notifications.html?hl=ja");
        // ブラウザ呼び出し
        Intent intent = new Intent(Intent.ACTION_VIEW,uri);
        // 通知に情報を与える PendingIntent の作成
        PendingIntent pendingIntent = PendingIntent.getActivity(MainActivity.this,0, intent,PendingIntent.FLAG_CANCEL_CURRENT);
        // 通知にアクションを設定
        builder.setContentIntent(pendingIntent);

        // NotificationManager で通知
        NotificationManager manager = (NotificationManager)MainActivity.this.getSystemService(NOTIFICATION_SERVICE);
        // 第一引数は、アプリケーション内でユニークな通知の識別子
        manager.notify(0, builder.build());

    }

ボタンは今回は、単純に Button の属性で指定しています。public void メソッド(View view) な定義ならなんでもいいです。







posted by lightbox at 2017-05-28 15:44 | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

2017年05月26日


Android Studio : BroadcastReceiver を Anonymous Inner Class で使用し、バッテリーの状態をエミュレータで簡単にテスト

▼ log.i の出力
I/lightbox: android.intent.action.BATTERY_CHANGED
I/lightbox: level : 10 ( / 100 )
I/lightbox: GOOD
I/lightbox: android.intent.action.BATTERY_CHANGED
I/lightbox: level : 50 ( / 100 )
I/lightbox: GOOD
I/lightbox: android.intent.action.BATTERY_CHANGED
I/lightbox: level : 100 ( / 100 )
I/lightbox: GOOD
内部クラスや、サブクラスで実装してもいいですが、内容としてはあまりしょっちゅう行うものでも無いし、テストとしては、telnet で adb に power capacity するほうが重要なのでこのような感じになりました。 BroadcastReceiver に代入する処理は、通常のイベント作成と同じく、new の後で CTRL+SPACE で候補を表示させて、BroadcastReceiver を選択すれば自動的に public void onReceive が作成されるので、その中に処理を記述するたけです。
package com.example.lightbox.broadcasttest;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

    private BroadcastReceiver broadcastReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.i("lightbox", "onResume");

        broadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {

                String action = intent.getAction();
                Log.i("lightbox", action);

                int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL,-1);
                int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE,-1);
                Log.i("lightbox", String.format("level : %d ( / %d )",level,scale));
                int health = intent.getIntExtra(BatteryManager.EXTRA_HEALTH,-1);
                switch( health ) {
                    case BatteryManager.BATTERY_HEALTH_COLD:
                        Log.i("lightbox", "COLD");
                        break;
                    case BatteryManager.BATTERY_HEALTH_DEAD:
                        Log.i("lightbox", "DEAD");
                        break;
                    case BatteryManager.BATTERY_HEALTH_GOOD:
                        Log.i("lightbox", "GOOD");
                        break;
                    case BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE:
                        Log.i("lightbox", "OVER_VOLTAGE");
                        break;
                    case BatteryManager.BATTERY_HEALTH_OVERHEAT:
                        Log.i("lightbox", "OVERHEAT");
                        break;

                }

            }
        };

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(MainActivity.this.getIntent().ACTION_BATTERY_CHANGED);
        // レシーバーを登録
        MainActivity.this.registerReceiver(broadcastReceiver,intentFilter);

    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.i("lightbox", "onPause");

        MainActivity.this.unregisterReceiver(broadcastReceiver);
    }
}

実際には switch 部分すら必要ありませんが、Android のドキュメントを覘くためのヒントとして使っています。

1) BatteryManager
2) BatteryManager.EXTRA_LEVEL

adb.exe がある場所

Android Studio で実行して、エミュレータ上で実行されたら、タスクマネージャでプロセスを表示してから、イメージ名をクリックしてソートすると、adb.exe がほぼ一番上に表示されるので、右クリックから『ファイルの場所を開く』を選択すると、でエクスプローラで開く事ができます



さらに、エクスプローラでそのフォルダを選択して SHIFT キーを押しながら右クリックして、『コマンドウインドウをここで開く』を選択します。

telnet を使えるように

既に使用している場合は必要無いですが、昨今めったに使う事が無いので Windows で使用できないようになっています。『プログラムと機能』の左サイドにある『Windows の機能の有効化または無効化』で開いたツリーで telnet を有効にします



コマンドプロンプトより adb にアクセス

まず、adb devices をコマンドプロンプトで実行して、現在のエミュレータの id を確認します。
List of devices attached
emulator-5554   device
次に、telnet localhost 5554 を実行して、telnet で adb にアクセスします。
Android Console: Authentication required
Android Console: type 'auth ' to authenticate
Android Console: you can find your  in
'C:\Users\lightbox\.emulator_console_auth_token'
ここで、まず auth コマンドでトークンを入力する必要があります。表示された場所にある、.emulator_console_auth_token をテキストエディタで開いてトークンを取得し、auth コマンドで実行します。
auth トークン文字列
Android Console: type 'help' for a list of commands
OK
これでコマンドが使えるようになるので、help と入力して確認します
help
Android console command help:

    help|h|?         print a list of commands
    event            simulate hardware events
    geo              Geo-location commands
    gsm              GSM related commands
    cdma             CDMA related commands
    crash            crash the emulator instance
    kill             kill the emulator instance
    network          manage network settings
    power            power related commands
    quit|exit        quit control session
    redir            manage port redirections
    sms              SMS related commands
    avd              control virtual device execution
    qemu             QEMU-specific commands
    sensor           manage emulator sensors
    finger           manage emulator finger print
    debug            control the emulator debug output tags
    rotate           rotate the screen clockwise by 90 degrees

try 'help ' for command-specific help
使用するのは、power コマンドです。
power
allows to change battery and AC power status

available sub-commands:
    display          display battery and charger state
    ac               set AC charging state
    status           set battery status
    present          set battery present state
    health           set battery health state
    capacity         set battery capacity state
power capacity 10 のようにして、バッテリーの量を変更するコマンドをクリップボードにコピーしておいて、貼り付けて実行してみて下さい。エミュレータの右上のバッテリーアイコンの量の表示が変わります。 すると、LogCat にも表示されるはずです
posted by lightbox at 2017-05-26 21:58 | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

2017年05月14日


Android Studio : OkHttp v3.8.0 で WEBアプリに POST 送信を行う

関連する記事

OkHttp v3.8.0 jar( と okio 1.13.0.jar ) を Eclipse のプロジェクトに追加して一般的な POST 送信を行う

Eclipse の一般的な Java からの送信を AsynTask を使用して Android Studio から送信しました。送信する文字列は、ArrayList にセットして、AsyncTask<ArrayList<String>,Void,String> として定義して引き渡しています。
package com.example.lightbox.posttest;

import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import java.util.ArrayList;

import okhttp3.Call;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = (Button) MainActivity.this.findViewById(R.id.button);
        button.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {

        if ( v.getId() == R.id.button) {
            Log.i("lightbox", "クリックされました");
            CallPost();

        }

    }

    private void CallPost() {

        ArrayList<String> data = new ArrayList<String>();
        data.add("winofsql@gmail.com");
        data.add("OkHttp で送信テスト(Android Studio)");
        data.add("日本語\r\n表示");

        AsyncTask<ArrayList<String>,Void,String> async;
        // 非同期実行用オブジェクト
        async = new AsyncTask<ArrayList<String>, Void, String>() {
            @Override
            protected String doInBackground(ArrayList<String>... params) {

                ArrayList<String> data = params[0];

                // HTTP 処理用オプジェクト
                OkHttpClient client = new OkHttpClient();

                // POST 用 FormBody の内容の作成
                FormBody.Builder formbodyBuilder = new FormBody.Builder();
                formbodyBuilder.add("to", data.get(0));
                formbodyBuilder.add("subject", data.get(1));
                formbodyBuilder.add("body", data.get(2));

                // 送信用ユニットの作成
                FormBody formbody = formbodyBuilder.build();

                // 送信用のデータを作成
                Request.Builder requestBuilder = new Request.Builder();
                String url = "https://ドメイン/lightbox/mail/send.php";
                requestBuilder.url(url);
                requestBuilder.post(formbody);
                Request request = requestBuilder.build();

                // 受信用のオブジェクトの準備
                Call call = client.newCall(request);
                String result = "";

                // 送信と受信
                try {

                    Response response = call.execute();
                    result = response.body().string();

                } catch (Exception e) {
                    e.printStackTrace();
                }

                return result;
            }

            @Override
            protected void onPostExecute(String s) {
                Log.i("lightbox", s);
                // 画面に対してのアクセスはここから行います


            }
        };

        // 実行
        async.execute(data);

    }

}


Module の build.gradle
apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.example.lightbox.posttest"
        minSdkVersion 23
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    testCompile 'junit:junit:4.12'
    compile 'com.squareup.okhttp3:okhttp:3.8.0'
}

※ AndroidManifest.xml には、<uses-permission android:name="android.permission.INTERNET" /> を追加。


posted by lightbox at 2017-05-14 20:07 | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする

2017年03月31日


Android Studio の .android .AndroidStudio2.x .gradle フォルダの場所を『ほぼ変更』する

変更した後、プロジェクトをロードすると、エラーが出たりしましたが、プロジェクト内のキャッシュが元のどこかを指してるようだったので、clean project(build) で対応できました。

前提条件

Windwos7 64ビットです。

検証環境は、Android Studio 2.3 ( Windows ) です。インストールファイルは、『android-studio-bundle-162.3764568-windows.exe Android SDK を含む(推奨)』です。

まずインストールフォルダを 『D:\Android Studio』にして、SDK フォルダを 『D:\sdk』で開始しています。

※ 過去バージョンのダウンロードはこちらです

結論から言うと

.AndroidStudio2.3 フォルダと、.gradle フォルダは消えてくれません。中身はそっくり D に移ったのですが、とてもバグっぽい挙動で勝手にフォルダを作成してしまうようです。

▼ こんな記録もありますし
Android Studio creates empty system directory in the default location after idea.system.path is set to a new location

.android フォルダは、インストール時に使うようで、それらしいファイルが残ってましたので、何かあった時の為に残していますが、詳しい検証・調査は行っていません。



.android フォルダは、エミュレータフォルダが作成される場所なので、それは完全に移動されています。

.android フォルダ

これはたぶん一般的なもので、ANDROID_SDK_HOME=D:\sdkhome という環境変数を作成して移動しました。D:\sdkhome は事前に作成して実行すると、設定する前に作成したエミュレータは AVD マネージャから見えなくなりました。元々のファイル郡は、.android\avd の中にありましたが、トラブルは嫌なので新規で作成しました。



※ エミュレータが作成されるので、ここが容量をとても喰うと思います。

.AndroidStudio2.3 フォルダ

これを実行するとこんなダイアログが出ました。



Android Studio がバージョンアップ時にいつも表示するアレです。


ここの中身を移動したい場合は、Android Studio\bin\idea.properties の中の設定を変更します
#
# *DO NOT* modify this file directly. If there is a value that you would like to override,
# please add it to your user specific configuration file.
#
# See http://tools.android.com/tech-docs/configuration
#
# Use ${idea.home.path} macro to specify location relative to IDE installation home.
# Use ${xxx} where xxx is any Java property (including defined in previous lines of this file) to refer to its value.
# Note for Windows users: please make sure you're using forward slashes (e.g. c:/idea/system).

#---------------------------------------------------------------------
# Uncomment this option if you want to customize path to IDE config folder. Make sure you're using forward slashes.
#---------------------------------------------------------------------
# idea.config.path=${user.home}/.AndroidStudio/config
idea.config.path=D:/sdkhome/idea/.AndroidStudio2.3/config

#---------------------------------------------------------------------
# Uncomment this option if you want to customize path to IDE system folder. Make sure you're using forward slashes.
#---------------------------------------------------------------------
# idea.system.path=${user.home}/.AndroidStudio/system
idea.system.path=D:/sdkhome/idea/.AndroidStudio2.3/system


idea.config.path と idea.system.path を直接変更しています。何も指定しないと、user.home は USERPROFILE 環境変数の値になるようです(${xxx} where xxx is any Java property)。で、その場所に、studio64.exe.vmoptions ファイルの中にある、-Didea.paths.selector=AndroidStudio2.3 と言う設定を使っていました。
#
# *DO NOT* modify this file directly. If there is a value that you would like to override,
# please add it to your user specific configuration file.
#
# See http://tools.android.com/tech-docs/configuration
#
-Xms256m
-Xmx1280m
-XX:ReservedCodeCacheSize=240m
-XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=50
-da
-Djna.nosys=true
-Djna.boot.library.path=

-Djna.debug_load=true
-Djna.debug_load.jna=true
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
-Didea.paths.selector=AndroidStudio2.3
-Didea.platform.prefix=AndroidStudio
-Didea.jre.check=true


これは、単純に文字列を結合するようです。-Didea.paths.selector=D:/sdkhome/idea/.AndroidStudio2.3 としたら、C:\Users\lightbox\.D:/sdkhome/idea/.AndroidStudio2.3 というパスでエラーが出ました。

idea.config.path と idea.system.path を指定した上で、-Didea.paths.selector=D:/sdkhome/idea/.AndroidStudio2.3 ならば、エラーは起こらずに .AndroidStudio2.3 フォルダは作成されません。が、これはたまたまエラー処理がスルーされただけなので、しないほうがいいでしょう。

面倒なので、idea.config.path と idea.system.path の指定のみにする事にしました。

.gradle フォルダ

これは、Android Studio の設定で指定します。



ただ、これも勝手に C:\Users\ユーザ名\.gradle\daemon\3.3 という空のフォルダを作成しちゃってますので注意して下さい。

参考にした記事

Changing IDE default directories used for config, plugins, and caches storage



タグ:Android Studio
posted by lightbox at 2017-03-31 18:53 | 2017 Android Studio | このブログの読者になる | 更新情報をチェックする
Seesaa の各ページの表示について
Seesaa の 記事がたまに全く表示されない場合があります。その場合は、設定> 詳細設定> ブログ設定 で 最新の情報に更新の『実行ボタン』で記事やアーカイブが最新にビルドされます。

Seesaa のページで、アーカイブとタグページは要注意です。タグページはコンテンツが全く無い状態になりますし、アーカイブページも歯抜けページはコンテンツが存在しないのにページが表示されてしまいます。

また、カテゴリページもそういう意味では完全ではありません。『カテゴリID-番号』というフォーマットで表示されるページですが、実際存在するより大きな番号でも表示されてしまいます。

※ インデックスページのみ、実際の記事数を超えたページを指定しても最後のページが表示されるようです

対処としては、このようなヘルプ的な情報を固定でページの最後に表示するようにするといいでしょう。具体的には、メインの記事コンテンツの下に『自由形式』を追加し、アーカイブとカテゴリページでのみ表示するように設定し、コンテンツを用意するといいと思います。


※ エキスパートモードで表示しています

アーカイブとカテゴリページはこのように簡単に設定できますが、タグページは HTML 設定を直接変更して、以下の『タグページでのみ表示される内容』の記述方法で設定する必要があります

<% if:page_name eq 'archive' -%>
アーカイブページでのみ表示される内容
<% /if %>

<% if:page_name eq 'category' -%>
カテゴリページでのみ表示される内容
<% /if %>

<% if:page_name eq 'tag' -%>
タグページでのみ表示される内容
<% /if %>
この記述は、以下の場所で使用します
container 終わり



フリーフォントで簡単ロゴ作成
フリーフォントでボタン素材作成
フリーフォントで吹き出し画像作成
フリーフォントではんこ画像作成
ほぼ自由に利用できるフリーフォント
フリーフォントの書体見本とサンプル
画像を大きく見る為のウインドウを開くボタンの作成

CSS ドロップシャドウの参考デモ
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり