カテゴリー別アーカイブ: アプリ開発

iPhoneプッシュ通知(APNs)の証明書を作る

iPhoneアプリでプッシュ通知するには、
対象のアプリのプッシュ通知証明書を毎年更新する必要がある。
Appleから取得できる証明書は「.cer」だが、サーバーで使うのは「.pem」なので、変換が必要になる。
やりかたを毎回忘れるのでその時に備えたメモ。

事前に済ませておくこと
・鍵ペアを作成する
・証明書の作成に必要な証明書署名要求を作る
・iOS Dev Centerで証明書を作成する
・キーチェーンアクセスに証明書を登録する

以下の手順でやる

・キーチェーンアクセスから証明書と秘密鍵の書き出しを行う
『証明書』からプッシュ通知用の証明書を右クリックして書き出す
apns1

ファイル名は「apns-dev.crt.p12」で保存
apns2

パスワード入力画面では、便宜上、空欄のままOKを押す
apns3

証明書に紐づく秘密鍵を右クリックして書き出す
apns4

ファイル名は「apns-dev.key.p12」で保存
apns5

パスワード入力画面では、便宜上、空欄のままOKを押す
apns6

証明書をPKCS12形式からPEM形式に変換する

cat apns-dev.crt.p12 | openssl pkcs12 -passin pass: -clcerts -nokeys > apns-dev.crt.pem

秘密鍵をPKCS12形式からパスワード付きのPEM形式に変換する(空パスワードは指定できない)

cat apns-dev.key.p12 | openssl pkcs12 -passin pass: -passout pass:PASS -nocerts > apns-dev-with-password.key.pem

秘密鍵からパスワードを除去する

cat apns-dev-with-password.key.pem | openssl rsa -passin pass:PASS > apns-dev.key.pem

証明書と秘密鍵を合成する

cat apns-dev.crt.pem apns-dev.key.pem > apns-dev.crt+key.pem

2015.9.6
NECO

cocos2d-xでゲーム画面を縦に変更

よく忘れるので、備忘録も兼ねてメモ

cocos2d-xでアプリを作る際、初期設定は横向きになっている。
iPhoneで縦画面としてレイアウトするには、以下のように行う。

(1) xcodeのナビゲータエリアから、xcodeプロジェクトファイルを選択し、エディタエリアからGeneral > Deployment Info > Device Orientation から、Portrait のみチェックを入れる。

layout

(2) $PROJ_ROOT/proj.ios_mac/ios/RootViewController.mmの
shouldAutorotateToInterfaceOrientationメソッドの戻り値を下記のように書き換える。

// Override to allow orientations other than the default portrait orientation.
// This method is deprecated on ios6
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return UIInterfaceOrientationIsPortrait( interfaceOrientation );
}

これにより、iPhoneで縦向きで表示される。

layout_portrait

ちなみに、macでも縦向きで表示するには、以下のファイルを編集する。試してないけどwindowsでもイケるはず。
$PROJ_ROOT/cocos2d/cocos/platform/desktop/CCGLViewImpl-desktop.cpp

GLViewImpl* GLViewImpl::create(const std::string& viewName)
{
    auto ret = new (std::nothrow) GLViewImpl;
//  if(ret && ret->initWithRect(viewName, Rect(0, 0, 960, 640), 1)) {
    if(ret && ret->initWithRect(viewName, Rect(0, 0, 320, 568), 1)) {
        ret->autorelease();
        return ret;
    }

    return nullptr;
}

これにより、macでも縦向きになる。

layout_portrait_mac

解像度がまちまちだったりとか、いろいろ問題もあるけど、
まずは縦画面にできた。

cocos2d-xでプロジェクトを作成

Mac環境での作業を想定。
事前にxcodeとpythonのインストールを済ませておく。

cocos2d-xのダウンロード
http://cocos2d-x.org/download

cocos2d-x-3.3.zipを任意のフォルダに展開

セットアップ
setup.py

新規プロジェクトの作成
cocos new [ゲーム名] -p [パッケージ名] -l cpp -d .

ビルドと実行
cocos run -p ios
cocos run -p android

ビルドが成功すれば、iOSシミュレータが起動し、アプリが起動する。
hello_cocos

その他のコマンドはヘルプを参照
cocos -help

Android の WebViewのキャッシュについて

本情報は、Android 2.3までのもので、3.0以上は未確認です。

WebViewアプリを作るうえで、
パフォーマンス向上のため、WebViewキャッシュに関して調べた。
その時のメモ。

◆キャッシュDBの場所
AndroidのWebViewのキャッシュは、dataフォルダ内に収められている。
WebViewCache.dbにsqlite形式で保存される、

/data/data/【パッケージ名】/databases/webviewCache.db

>sqlite3 webviewCache.db
sqlite> .schema
CREATE TABLE cache (

_id INTEGER PRIMARY KEY

, url TEXT, filepath TEXT

, lastmodify TEXT

, etag TEXT

, expires INTEGER

, expiresstring TEXT

, mimetype TEXT

, encoding TEXT

, httpstatus INTEGER

, location TEXT

, contentlength INTEGER

, contentdisposition TEXT

, crossdomain TEXT

, UNIQUE (url) ON CONFLICT REPLACE
);
sqlite> select * from cache;
_id|url|filepath|lastmodify|etag|expires|expiresstring|mimetype|encoding|httpstatus|location|contentlength|contentdisposition|crossdomain
1|http://www.darkdrive.net/***/style.css|7abbf48b|Mon, 18 Feb 2013 16:43:21 GMT||1365353616876||text/css||200||10055||
2|http://www.darkdrive.net/***/arrow.gif|2b518e19|Sun, 16 Sep 2012 06:55:56 GMT||1368039066000||image/gif||200||70||

◆ファイルの実体
また、キャッシュファイルの実体の保存場所は、
webviewCacheディレクトリ下になる。
ファイル名は、さきほどのDBに保存された名前(filepath)になっている。

/data/data/【パッケージ名】/cache/webviewCache
※ディレクトリ

>adb shell
# cd /data/data/***/cache/webviewCache
# ls -l
-rw——- app_xxx app_xxx 10055 2013-02-18 16:43 7abbf48b
-rw——- app_xxx app_xxx

70 2012-09-16 06:55 2b518e19

感想として、
WebViewのキャッシュは端末本体に保存されるため、保存領域や容量上限などで取り扱いづらい。
キャッシュデータを直接読み込めると、
ディスクIOの回数が減るので、全体的なパフォーマンス向上になったりするのかなぁと。
Android 3.0以降の調査がほとんどできてないので、そちらも調べてみたい。

2013.5.9
NECO

年末年始は無料恋愛ノベルアプリ「KIRIKA」を読もう!(女性向けです!)

年末年始って家にいる時間が多く、暇な時間を持て余すことがありますよね。
・・・ってそんな奴、私だけかもしれませんが。

ぜひ、そんな暇で退屈な時間がありましたら、
Playストアにて好評配信中の恋愛ノベルアプリ「KIRIKA~同じ人間がいる、もう一つの世界~」を
読んでみてください(><)

↓↓↓DLはこちらからどうぞ↓↓↓
https://play.google.com/store/apps/details?id=net.darkdrive.android.kirika

全70話(全部読むのに10~15時間ぐらいかかる、圧倒的ボリュームです!)を無料で配信しています。

本作品のジャンルは「恋愛ファンタジー」です。
30話ぐらいまでは恋愛要素は薄く、30話以降は恋愛面が強調されていきます。
そして、50話以降になるとキスシーンが多いです!

イベントシーンまでもが表情が切り替わり、立ち絵の表情パターンも尋常じゃないくらいに多いです!!

さらに、エンディングはマルチエンディングになっていまして、
一本道ノベルですが、最後だけ分岐します。
そして、一番読者から人気があり、且つルート投票で50票集まったものが、
無料で続編化されます!

ぜひ、読んで1票を投じていただけるとうれしいです。(選挙日だけに(笑))

ここまで読んでいただき、ありがとうございました。

2012.12.16
神森 真昼

※KIRIKAって何?
→全70話、エンディングまで完全無料の恋愛ノベルアプリです!

※内容の詳細、ダウンロードはこちら!
https://play.google.com/store/apps/details?id=net.darkdrive.android.kirika

検索用:恋愛ゲーム、小説、ノベル、女性向け、恋愛シミュレーション、ノベルゲーム、乙女ゲーム

【Androidアプリ作成】HTML5 Application Cacheを使ってみる

HTML5の「Application Cache」を使うと
WEBページのオフライン表示を比較的容易に実現できる。

この機能は、初めてアクセスした時に表示に必要なファイルを全部
ダウンロードし、次回以降は不必要な通信を行われずに
表示されるといった挙動となる。

オフラインでも表示できるのが最大のメリット。

いくつか対応に手間取ったので、備忘録も兼ねてメモ。

■HTMLにmanifestを記載
——————————
<html lang="ja" manifest="cache.appcache">
<head></head>
<body></body>
</html>
——————————
※便宜上「~.appcache」としているが別指定も可能

■cache.appcacheを作成
アクセスするディレクトリ直下にcache.appcacheを作る
——————————
CACHE MANIFEST

# Version 201207170123

## Cache Files
CACHE:
index.html
style.css
data/data.json
js/script.js

## Network Files
NETWORK:
*
——————————
一行目はCACHE MANIFEST固定
「CACHE:」以降にローカル保存するファイルを記載
「NETWORK:」以降は毎回取りに行くファイルを記載

■Content-Typeのmanifest対応
アクセスするディレクトリ直下に.htaccessを作り
以下の内容を記載。
——————————
AddType text/cache-manifest .appcache
——————————

■apacheの設定変更(必要に応じて)
——————————
<Directory />

Options All

AllowOverride All
</Directory>
——————————
※環境によっては'/'ではないことも。適宜 $DOCUMENT_ROOT に読み替える。
apache再起動も忘れずに。

■WebView対応
AndroidからWebView経由でアクセスする場合は追加で必要。
——————————
public class MainActivity extends Activity {

@Override

public void onCreate( Bundle savedInstanceState ) {

super.onCreate( savedInstanceState );

WebView webView = new WebView( this );

webView.getSettings().setAppCacheEnabled( true );

webView.getSettings().setAppCacheMaxSize( 32 * 1024 * 1024 );

webView.getSettings().setCacheMode( WebSettings.LOAD_CACHE_ELSE_NETWORK );

webView.loadUrl( "http://www.darkdrive.net/index.html?from=blog" );

ViewGroup vg = (ViewGroup)findViewById( R.id.vgApp );

vg.addView( webView );

}
}
——————————

ファイル更新時は、忘れずに cache.appcache を適宜書き換える。

本機能により通信しなくてもWEB表示できるようにはなるが、
あらかじめダウンロードを行う分、
最初のネットワークの使用量が多くなってしまう。

利便性を考慮すると、ユーザーが設定で切り替えられるように
したほうがいいかも。

2012.7.17
NECO

【Androidアプリ作成】SQLiteを使うには?(後編)

前回のページからの続きです。
見てない方は下記をご参照ください^^
http://blogs.yahoo.co.jp/hiro5_188/21863869.html

前回に引き続き、SQLiteのDBファイルからの読み込みです。
大体以下のようになります。

[1] アプリ起動時

(1-1) DBが既にあれば何もしない

(1-2) DBがなければ空のDBを作る

(1-3) assetsからコンテキストデフォルトのDBパスへコピー

(1-4) DB作成に失敗したらDB削除

[2] SQLite使用時

(2-1) DBを開く

(2-2) 自由に操作

(2-3) DBを閉じる

上記の処理を実際にプログラムすると
以下のような感じになります。
長いですが、全部書きます。

[1] アプリ起動時

public static boolean onCreate( Context context, String dbname )

{

if ( isExistsDataBase( context, dbname ) )

// (1-1)

{

// 既にDBが存在するなら何もしない

return true;

}

try

{

createEmptyDataBase( context, dbname );

// (1-2)

copyDataBaseFromAsset( context, dbname );

// (1-3)

return true;

}

catch ( IOException e )

{

try

{

deleteDataBase( context, dbname );

// (1-4)

}

catch ( Exception ignore ) {}

return false;

}

}

(1-1) DBが既にあれば何もしない

private static boolean isExistsDataBase( Context context, String dbname )

{

SQLiteDatabase con = null;

try

{

con = openDataBase( context, dbname );

// (2-1)

return true;

}

catch ( IOException ignore )

{

// 例外は飲み込む

return false;

}

finally

{

if ( con != null )

{

try { con.close(); } catch ( Exception ignore ) {}

con = null;

}

}

}

(1-2) DBがなければ空のDBを作る

private static void createEmptyDataBase( Context context, String dbname ) throws IOException

{

SQLiteOpenHelper openHelper = new SQLiteOpenHelper( context, dbname, null, 1 )

{

@Override

public void onUpgrade( SQLiteDatabase sqlitedatabase, int i, int j ) {}

@Override

public void onCreate( SQLiteDatabase sqlitedatabase ) {}

};

SQLiteDatabase db = null;

try

{

// このメソッドを呼ぶことで、空のデータベースが

// アプリのデフォルトシステムパスに作られる

db = openHelper.getReadableDatabase();

}

finally

{

if ( db != null )

{

try { db.close(); } catch ( Exception ignore ) {}

db = null;

}

}

}

(1-3) assetsからコンテキストデフォルトのDBパスへコピー

private static void copyDataBaseFromAsset( Context context, String dbname ) throws IOException

{

InputStream in = null;

OutputStream out = null;

try

{

// asset 内のデータベースファイルにアクセス

in = context.getAssets().open( dbname + ".sqlite" );

// デフォルトのデータベースパスに作成した空のDB

String dbpath = context.getDatabasePath( dbname ).getPath();

out = new FileOutputStream( dbpath );

// コピー

byte[] buffer = new byte[ 4 * 1024 ];

int size;

while ( ( size = in.read( buffer ) ) >= 0 )

{

out.write( buffer, 0, size );

}

out.flush();

}

finally

{

if ( out != null )

{

try { out.close(); } catch ( Exception ignore ) {}

out = null;

}

if ( in != null )

{

try { in.close(); } catch ( Exception ignore ) {}

in = null;

}

}

}

(1-4) DB作成に失敗したらDB削除

private static void deleteDataBase( Context context, String dbname )

{

String dbpath = context.getDatabasePath( dbname ).getPath();

File db = new File( dbpath );

if ( db.exists() )

{

db.delete();

}

}

[2] SQLite使用時
(2-1) DBを開く

public static SQLiteDatabase openDataBase( Context context, String dbname ) throws IOException

{

// Open the database

SQLiteDatabase db;

try

{

String dbpath = context.getDatabasePath( dbname ).getPath();

db = SQLiteDatabase.openDatabase( dbpath, null, SQLiteDatabase.OPEN_READONLY );

}

catch ( SQLiteException e )

{

// あえて検査例外で投げ直す

throw new IOException( e.toString() );

}

return db;

}

(2-2) 自由に操作

Cursor rs = con.rawQuery( "select ITEM_ID, ITEM_NAME from ITEM_INFO", null );

rs.moveToFirst();

int len = rs.getCount();

ArrayList<ItemDao> list = new ArrayList<ItemDao>( len );

while ( 0 <= (–len) )

{

int offset = 0;

int id = rs.getInt( offset++ );

String name = rs.getString( offset++ );

list.add( new ItemDao( id, name ) );

rs.moveToNext();

}

return list;

(2-3) DBを閉じる

db.close();

結構手軽に扱えるので、お試しください^^

2011.12.23
NECO

【Androidアプリ作成】SQLiteを使うには?(前編)

大量のデータを使う際にはSQLを使うのが便利。
WEBサービスではデータの定義や永続化にSQLを使うことが多いと思われるが、
Androidでも小規模ながらSQLを使うことができる。

■データの定義
データの定義はPCからできる。
今回はWindowsで行った。

sqlite自体はAndroidSDKに付属してる。
バージョンは 3.6.22 のようです。("sqlite3 -version"で確認できる)
その気になればコマンドラインからでも操作できますが、
今回はめんどくさいので管理ツールを使う。

■各種インストール
・Apache
http://httpd.apache.org/
2.0.63 入れた。
[httpd.conf]
LoadModule php5_module C:\php5\php5apache2.dll

<IfModule mod_php5.c>
AddType application/x-httpd-php .php
PHPIniDir "C:/php5"
</IfModule>

・PHP
http://www.php.net/
5.3.1 入れた。

[php.ini]
必要なextentionは3つ
・php_mbstring.dll
・php_pdo_sqlite.dll
・php_sqlite3.dll

・SQLiteManager
http://www.sqlitemanager.org/
1.2.4 入れた。
ついでに日本語化。

■データ作成
・DBの作成
以下のコマンドで作れる

>sqlite3 test.sqlite

".quit"で抜けれる。

SQLiteManagerへの登録
SQLiteManagerのdocbaseと同じ階層に
DBファイルを置く。
ブラウザから名前、パスにDBファイル名を指定。
アップロードは非チェック。
私の環境ではなぜかアップロード出来なかった。
よくわからない。

・テーブルの作成
自由に CREATE TABLE すればいいと思う。

Android用に、別途
android_metadataなるテーブルが必要。
無いとアプリ起動時に例外が出る。
CREATE TABLE android_metadata ( locale text default 'en_US' );

・データの登録
自由に INSERT INTO すればいいと思う。

■データをプロジェクトディレクトリにコピー
SQLiteManagerのdocbaseからAndroidプロジェクトディレクトリに
DBファイルをコピーするバッチファイル作る。
コピー先は$PROJECT/assets以下とかで。

…とりあえず読み込む準備ができたので、
Androidから読み込む処理はまた今度。

2011.12.20
NECO

【Androidアプリ作成】@stringをHTMLタグを使ってデコレーションする

@stringのデータに一部のHTMLタグが使えます。
ただフォントサイズによっては効いているのかわかりづらいです。
(私の環境では<b>の効果が全く分かりませんでした。。)

【android developers:String-resource】
http://developer.android.com/intl/ja/guide/topics/resources/string-resource.html

サンプルソース)
<?xml version="1.0" encoding="utf-8"?>
<resources>

<string name="welcome">Welcome to <b>神森真昼の小説と波の世界</b>!</string>
</resources>

その他にも<U>(アンダーライン)と<I>(イタリック)が使えます。
ぜひ試してみてください^^>

2011.7.24
神森 真昼

【Androidアプリ作成】単純なコードでエラーが出るときに確認したいポイント

開発をはじめてからもうすぐ3ヶ月。私が最初の方でつまづいたちょっとしたエラーたちをご紹介します。
(初心者向けなのでプログラムに詳しい方は読み飛ばしてください^^)

【 なぜこんな単純なソースでエラーが?? 】

最初の頃は(今でもそうですが)、XMLレイアウトを簡単に組んで実行して意図したとおりに
動いているか確認したいものです。それで簡単にえい、や!で作成したサンプルコードが
下記のものです。

エラーが出るサンプルコード)
<LinearLayout

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:orientation="horizontal"

android:layout_gravity="center"

android:layout_marginBottom="10sp">

<TextView

android:text="あああ"

android:textStyle="bold"

android:layout_gravity="center">

</TextView>

<TextView

android:text="いいい"

android:textStyle="bold"

android:layout_gravity="center"

android:layout_marginLeft="5sp">

</TextView>
</LinearLayout>

書いてみても特に構文エラー(書いて保存すると赤線引かれしまうアレです)
も出ないし起動してみると、エラーが出ます><
なんで、XMLでエラーでるんだよぉ!と嘆いたことありませんか。

このエラーはTextViewなどのタグの必須要素が指定されてないので
エラーが出たわけです。

必須要素はlayout_widthとlayout_heightです。
指定するときはいつでもこのお二方を入れてあげてください。

エラーを修正したサンプルコード)

<LinearLayout

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:orientation="horizontal"

android:layout_gravity="center"

android:layout_marginBottom="10sp">

<TextView

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="あああ"

android:textStyle="bold"

android:layout_gravity="center">

</TextView>

<TextView

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="いいい"

android:textStyle="bold"

android:layout_gravity="center"

android:layout_marginLeft="5sp">

</TextView>
</LinearLayout>

【 文字列を差し替えただけなのに動かない 】

とりあえずレイアウトがうまくいったので、
@stringを使って日本語と英語によって振り分けしようと文章を入れ替えたら
動かないということは今までにありませんでしたか。

こんな指定をすると構文エラーがでますので、注意しましょう。
<string name="param1">1. Let's Try Again</string>
<string name="param2">2. ' please touch me '</string>

何がいけないのかというと ' (アポストロフィー)が使われていることです。
アポストロフィーはプログラムで使用しますので、使うとプログラムだと解釈されて
「こんなところに書いてくれるなー!」と怒られてしまうわけですね。
(他の言語だと文字列に書くスペースなので、そんな解釈されることもない場合もあるのですが
Androidはエラーを出すプログラムのようです。)

じゃあ、アポストロフィーを使った表現は避けて記述しなきゃだめなのでしょうか。
いいえ、そんなことはありません。
Let's TryやI'mという表現もよく使う英文だし、使えないとうっとおしくて仕方ありません。

そんなときのためにあるのがエスケープシーケンス。
さきほどのエラーが出る文字列にエスケープシーケンスを入れてみましょう。

<string name="param1">1. Let\'s Try Again</string>

こんな感じに入れて実行すると、Let's Try Again と表示されます。
\を入れることで「この文字はプログラムに出てくるかもしれないけど、文字列ですから!!」と主張できるんです。

ちなみに\マークを表示させたい場合は、\をもう一つ記述します。
「\\」で「\」と表示されます。

2011.7.22
神森 真昼