20200518のAndroidに関する記事は4件です。

ブラウザでのサーバー証明書確認方法[Android/Chrome]

サーバー証明書確認方法

Chrome(Android)でのサーバー証明書確認

「鍵マーク」をタップ(軽くタッチ)する

Screenshot_20200518-070253.jpg

「詳細」をタップ

Screenshot_20200518-070258.jpg

「証明書情報」をタップ

Screenshot_20200518-070304.jpg

「証明書ビューア」で確認

赤色枠で囲まれた「発行先」の「組織(O)」がウェブサイトの運営組織と同一か確認する。
青色枠で囲まれた「発行元」の「組織(O)」(認証局)が「発行先」の確認をBaseline Requirementsという国際的な審査基準に従い審査し発行している。
「発行先」に「組織(O)」が含まれている証明書は「EV(Extended Validation)証明書」または「OV(Organization Validation)証明書」であり、信頼性の高い証明書といえる。ウェブサイトの運営組織と一致していれば、問題のないウェブサイトだといえる。

Screenshot_20200518-220059.jpg
もし、「発行先」に「一般名(CN)」と「シリアル番号」しかない場合、それは「DV(Domain Validation)証明書」と呼ばれる。「発行元」(認証局)より「組織(O)」の審査が行われていない証明書となる。この「DV証明書」の場合、信頼性は低いため、金融機関で使われることはほとんどなく、個人情報、クレジットカード情報の入力をするべきではない。
参考:【重要】住信SBIネット銀行を装ったフィッシングメールにご注意ください
参考:ドコモを装ったメールにご注意ください!
Screenshot_20200518-221000.jpg

Windows版Chromeでの確認方法は以下参照のこと。
証明書の「サブジェクト」(発行先)の「O」(組織)の名前を確認してウェブサイトの安全性を確認できる。
参考:Google ChromeでWebサイトのSSLサーバ証明書を調査、確認する

サーバー証明書の確認とウェブサイトの安全性を確認できる。
参照:SSL Labs

参照:Baseline Requirements
参照:CA/Browser Forum参加メンバー

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

View#getGlobalVisibleRect()は戻り値を確認しなさいという話

一発ネタです

Viewの表示されている位置が欲しい場合、View#getGlobalVisibleRect(rect) を利用する場合があると思います。このrectが正しい値を示すのは、戻り値がtreuの時だけですよというお話。
表示範囲外にある場合は、戻り値がfalseになり、rectには計算途中の値が入った状態で戻るため、この値を元に何かを判断してはいけません。画面外にあるのに画面内にあるような値が入ってくることがあります。

言いたいことは以上なのですが、以下解説

実験

以下のようなレイアウトを作ります。何でもいいですが、Viewが画面に表示されない位置に移動できるようにしておきます。

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/scroll_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >

        <View
            android:id="@+id/view"
            android:layout_width="200dp"
            android:layout_height="50dp"
            android:layout_gravity="center_horizontal|bottom"
            android:layout_marginTop="1000dp"
            android:background="#ff0000"
            />
    </FrameLayout>
</ScrollView>

では、このid/viewの位置を取ってみましょう。
以下のように画面サイズの高さと併せて見てみます。

scroll_view.setOnScrollChangeListener { _, _, _, _, _ ->
    val size = Point()
    windowManager.defaultDisplay.getSize(size)
    val rect = Rect()
    view.getGlobalVisibleRect(rect)
    Log.e("XXXX", "getGlobalVisibleRect: ${rect.top} ${size.y}")
}

これをゆっくりスクロールさせると

E/XXXX: getGlobalVisibleRect: 1588 1794
E/XXXX: getGlobalVisibleRect: 1587 1794
E/XXXX: getGlobalVisibleRect: 1584 1794
E/XXXX: getGlobalVisibleRect: 1791 1794 ←
E/XXXX: getGlobalVisibleRect: 1788 1794
E/XXXX: getGlobalVisibleRect: 1787 1794

このように、画面の高さよりもrect.topの値が小さくなってもViewは見えません、そして見えるようになると急に値が変わって、画面高さよりも少し小さく、頭が見えた位置らしい値になっていそうです。

getGlobalVisibleRectの戻り値も併せて表示するとこうなります。

E/XXXX: getGlobalVisibleRect: false 1590 1794
E/XXXX: getGlobalVisibleRect: false 1587 1794
E/XXXX: getGlobalVisibleRect: false 1585 1794
E/XXXX: getGlobalVisibleRect: true 1793 1794 ←
E/XXXX: getGlobalVisibleRect: true 1791 1794
E/XXXX: getGlobalVisibleRect: true 1788 1794

画面上に表示されていない状態ではfalse、表示されるようになるとtrueになってrectの値をも確からしい値になります。

なぜか

コードを追ってみます。まずはゼロ座標に左上がある前提でrectに格納し、親のgetChildVisibleRectをコールしています。

View.java
public final boolean getGlobalVisibleRect(Rect r) {
    return getGlobalVisibleRect(r, null);
}

public boolean getGlobalVisibleRect(Rect r, Point globalOffset) {
    int width = mRight - mLeft;
    int height = mBottom - mTop;
    if (width > 0 && height > 0) {
        r.set(0, 0, width, height);
        if (globalOffset != null) {
            globalOffset.set(-mScrollX, -mScrollY);
        }
        return mParent == null || mParent.getChildVisibleRect(this, r, globalOffset);
    }
    return false;
}

ではgetChildVisibleRectは何をやっているかというと

ViewGroup.java
public boolean getChildVisibleRect(
        View child, Rect r, android.graphics.Point offset, boolean forceParentCheck) {
    final RectF rect = mAttachInfo != null ? mAttachInfo.mTmpTransformRect : new RectF();
    rect.set(r);

// 略

if ((forceParentCheck || rectIsVisible) && mParent != null) {
        if (mParent instanceof ViewGroup) {
            rectIsVisible = ((ViewGroup) mParent)
                    .getChildVisibleRect(this, r, offset, forceParentCheck);
        } else {
            rectIsVisible = mParent.getChildVisibleRect(this, r, offset);
        }
    }
    return rectIsVisible;
}

長いので省略しますが、要するに子Viewの相対的な位置から自分のViewの上のオフセットなどを計算し、「見えている場合」さらに親のgetChildVisibleRectをコールしています。最終的にRootのViewまで到達するとViewの全体から見た位置が分かると言うことですね。

逆に、どこかの親Viewからはみ出ていたりする場合、つまり「見えていない」と分かった段階で、親へ親への伝達が止まります。そのためオフセット計算が途中の信頼できない値が入っている状態になります。

https://developer.android.com/reference/android/view/View#getGlobalVisibleRect(android.graphics.Rect)

Returns
boolean true if r is non-empty (i.e. part of the view is visible at the root level.

ドキュメントはちゃんと読みなさいということでした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Kotlin】Androidの回転時のカメラプレビュー処理

はじめに

初投稿です。今回はテストも兼ねてAndroidのカメラプレビューの回転処理をどうするか問題について書いてみます。
私自身初心者ですので内容も初心者向けかもしれませんが、少しでも参考になれば幸いです。

スマホ画面の向きを取得

fun rotationView(){
        appRotation = when(windowManager.defaultDisplay.rotation){//アプリ画面の向き
            Surface.ROTATION_0 -> 0
            Surface.ROTATION_90 -> 90
            Surface.ROTATION_180 -> 180
            Surface.ROTATION_270 -> 270
            else -> //ありえない
        }
・
・

縦向きと横向きの2通りにしてもいいです。

回転時ビューをリサイズするので倍率を決める

後述しますが、videoWidth,videoHeightにはSurfaceTextureの横,縦のサイズが入っています。

・
・
        val scaleX:Float = when{
            appRotation == 0 || appRotation == 180 -> videoWidth.toFloat() / videoWidth.toFloat()//1.0倍(縦向きならリサイズしない)
            appRotation == 90 ||appRotation == 270 -> videoWidth.toFloat() / videoHeight.toFloat()//X/Y倍(横向きならリサイズする)
            else -> 1.0f//ありえない
        }
        val scaleY:Float = when{
            appRotation == 0 || appRotation == 180 -> videoHeight.toFloat() / videoHeight.toFloat()//1.0倍(縦向きならリサイズしない)
            appRotation == 90 || appRotation == 270 -> videoHeight.toFloat() / videoWidth.toFloat()//Y/X倍(横向きならリサイズする)
            else -> 1.0f//ありえない
        }
・
・

scaleXが横軸(スマホの短い方),scaleYが縦軸(スマホの長い方)の倍率です。

最後にビューを回転,リサイズする

・
・
        val matrix = Matrix()
        matrix.postRotate(-appRotation.toFloat(),videoWidth * 0.5f,videoHeight * 0.5f)//px,pyを起点にスマホの回転と逆回転させる
        matrix.postScale(scaleX,scaleY,videoWidth * 0.5f,videoHeight * 0.5f)//px,pyを起点にリサイズ
        mainView.setTransform(matrix)
    }

postRotateでスマホの回転方向と逆向きにビューを回転させます。ただ回転させるだけだとビューが引き延ばされた感じになってしまうので、postScaleで横向きのときはX軸にX/Yを掛けてYの長さに、Y軸にY/Xを掛けてXの長さにリサイズします。
単純にビューの縦横のサイズを指定できれば早いんですけどね...。

あとはsurfaceTextureListenerでサイズを変更する

private val surfaceTextureListener = object:TextureView.SurfaceTextureListener{
        override fun onSurfaceTextureAvailable(texture:SurfaceTexture, width: Int, height: Int) {
            //有効になった時
            videoWidth = width
            videoHeight = height
            rotationView()
        }
        override fun onSurfaceTextureSizeChanged(texture:SurfaceTexture, width: Int, height: Int) {
            //サイズが変わった時
            videoWidth = width
            videoHeight = height
            rotationView()
        }
        override fun onSurfaceTextureUpdated(texture:SurfaceTexture) {
            //更新時
        }
        override fun onSurfaceTextureDestroyed(texture:SurfaceTexture): Boolean {
            //破棄された時
        }
    }

ビューのサイズが変わったときもwidthとheightを渡すなりすればちゃんとリサイズしてくれます。
もっとこうした方がいいよってのがあれば是非教えてください!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Android Studio】 SQLiteを使いやすくするクラス テーブルの作成・削除 

概要

Android Studioには、データベースを用いたアプリケーションを作りやすくするために「SQLiteOpenHelper」クラスや「SQLiteDatabase」クラスなどのクラスが用意されています。
これらのクラスはとても便利で使いやすいのですが、

・テーブルを作る際に既に同じ名前のテーブルが存在しないかチェックする必要がある。
・SQLをコード中に直接書く必要がある。
・SQLはStringオブジェクトとして書く必要があり、間違ってコーディングしたときに気づきにくい。
・Stringオブジェクトとして書いたSQLが邪魔でちょっと見ずらい。

という点で不便だなと思うことがありました。
中でも、私はもともとwebアプリケーションを作っていたので生のSQLがコードにあると不安になってしまうというきらいがあります。
また、何がしているのか直感的に分かるコードが好きでもあります。
ということで今回は、関数チックにSQLを使えてメインのプログラムを見たときに何をしているのか分かりやすいものであることを目標にして「SQLiteDatabase」クラスをサポートするクラスを作っていこうと思います。

ソースコード

「controlTable」クラス

controlTable.java
package com.example;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

public class controlTable implements SQLiteSupporter {
    public void createTable(SQLiteDatabase dbObject, String tableName, String[][] list) {
        if (!getTableExist(dbObject,tableName)) {
            StringBuilder createTable = new StringBuilder("CREATE TABLE ");
            for (String[] str :list) {
                if (str == list[0]) {
                    String s = tableName + "(" + str[0] + " " + str[1] + " PRIMARY KEY";
                    createTable.append(s);
                } else {
                    String s = ", " + str[0] + " " + str[1];
                    createTable.append(s);
                }
            }
            createTable.append(")");
            dbObject.execSQL(createTable.toString());
        }
    }

    public void dropTable(SQLiteDatabase dbObject,String tableName) {
        if (getTableExist(dbObject,tableName)) {
            String dropTable = String.format("DROP TABLE IF EXISTS '%s'",tableName);
            dbObject.execSQL(dropTable);
        }
    }

    private boolean getTableExist(SQLiteDatabase dbObject, String tableName){
        String query = String.format("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='%s';", tableName);
        Cursor c = dbObject.rawQuery(query, null);
        c.moveToFirst();
        String result = c.getString(0);
        return !result.equals("0");
    }
}

「SQLiteSupporter」というインターフェイスを継承していますが、メインプログラムが読みやすくなるためと後で機能を変えやすくするためですので継承させなくても構いません。

今回はテーブルの作成と削除だけですので、非常にシンプルですね。
テーブルの作成はcreateTableメゾットで、削除はdropTableメゾットで行います。テーブルの有無はgetTableExistメゾットで行います。
これらの使い方は後ほど具体例を交えて説明するとして、大まかにソースコードの中身をいうと基本的には受け取った引数をもとにSQLを組み立ててそれを実行するといった感じですね。これは後々作るであろう他のメゾットでも変わらないと思います。

「controlTable」クラスが継承しているインターフェイス

SQLiteSupporter.java
package com.example;

import android.database.sqlite.SQLiteDatabase;

public interface SQLiteSupporter {
public void createTable (SQLiteDatabase dbObject, String tableName, String[][] list) ;
public void delateTable (SQLiteDatabase dbObject,String tableName);
}

これも特に説明するところはないですね。ただ、引数を決め過ぎたかなーと思ったのでオーバーロードしてテーブルを全て消す機能などを追加してもいいかなと個人的には思いました。

各メゾットの使い方

createTableメゾット

createTableには引数が三つあります。
一つ目は、「SQLiteDatabase」クラスのインスタンスです。注意点はopenOrCreateDatabaseメゾットを使った後に入れるということです。

SQLiteDatabase dbObject;
String dbName ;

/////////////////////////////////////////////////////////////////////////////////////
dbName = "data/data/" + getActivity() + "/myDatabase1.db";
dbObject = SQLiteDatabase.openOrCreateDatabase(dbName,null);

私の場合はフラグメント内で使いたかったのでgetActivityメゾットを使ってデータベースの場所と取得しました。データベースの名前もデフォルトのままですのでmyDatabase1.dbで取得できています。
参考までにコードを載せてはいますが、ここに関しては使う場所などによって取得方法に違いがでると思います。

二つ目の引数の名前はテーブル名の名前です。Stringオブジェクトとして好きな名前を入れて下さい。

string name = "table";

三つ目の引数はテーブルの項目です。下記のようなStringオブジェクトの配列で入れて下さい。一番最初の項目が主キーになります。
{項目名,項目のデータ形}です。

String[][] data = {
                    {"id", "INTEGER"},
                    {"type", "STRING"},
                    {"int", "INTEGER"},
                    {"date", "STRING"},
                    {"category", "STRING"},
                    {"tag", "STRING"}
                  };

createTableメゾットにこれらの引数を入れるとテーブルを作ることが出来ます。

SQLiteDatabase dbObject;
String dbName ;


dbName = "data/data/" + getActivity() + "/myDatabase1.db";
dbObject = SQLiteDatabase.openOrCreateDatabase(dbName,null);
string name = "table";
String[][] data = {
                    {"id", "INTEGER"},
                    {"type", "STRING"},
                    {"int", "INTEGER"},
                    {"date", "STRING"},
                    {"category", "STRING"},
                    {"tag", "STRING"}
                  };
SQLiteSupporter db = new controlTable();
db.createTable(dbObject,name, data);

delateTableメゾット

createメゾットとだいたい一緒です。SQLiteDatabaseクラスのインスタンスとテーブルの名前を入れて下さい。

SQLiteDatabase dbObject;
String dbName ;


dbName = "data/data/" + getActivity() + "/myDatabase1.db";
dbObject = SQLiteDatabase.openOrCreateDatabase(dbName,null);
string name = "table";

SQLiteSupporter db = new controlTable();
db.dropTable(dbObject,name);

まとめ

テーブルを作るのが一つだけでいいという場合はSQLを直接書いた方がいいと思います。また、簡単な構造で必要なコード数が少ない場合もSQLを直接書いた方が余計なファイルを増やさずに済むのでいいと思います。
ただ、ボタン操作と関連付けたり、頻繁にテーブル操作を行う必要があったりする場合などはコードが煩雑になりやすいので今回のようにしてみてもいいのではないかと思います。
あとは、SQLはメゾットが組み立ててくれるのでテーブル名を変更したい場合や、項目の数や名前を変えたい場合はかなり楽に変えることができるので変更にも強くなります。実際、試しにSQLを直接書いたコードと今回のコードを作ってみ比べてみたのですが、目に見えて効果を感じることが出来ました。

ただ、createTableメゾット本体は外部キーには対応できていないので拡張の余地が残っているなと思います。
また、今回のような簡単なテーブル操作の場合だと可読性や保守性の向上という強みは生かされにくいので他の操作にも対応したものを作っていこうかなと思います。

拙いところや不具合等がありましたらコメント欄にてご指摘・ご指導のほどよろしくお願いします。では、失礼します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む