ラベル Android の投稿を表示しています。 すべての投稿を表示
ラベル Android の投稿を表示しています。 すべての投稿を表示
2015/01/26

[Android] Support Library に追加された testing-support-lib や Espresso 2.0 で JUnit4 な Android のテストを書く

Android Support Library に UI テストを行う Espresso 2.0 や
JUnit4 でテストを行うための testing-support-lib が追加されました。

前置き

概要とドキュメントなど

Espresso 2.0 is here!

android-test-kit - Google's Testing Tools For Android - Google Project Hosting


サンプルコード

googlesamples/android-testing


関連エントリ

Espresso 2.0 が Android support library の一部としてリリースされた - ひだまりソケットは壊れない
Espressoがsupport libraryになってAndroidでJUnit4が使えるようになったと聞いたので試してみた - みんからきりまで
続・AndroidでJUnit4を使う方法(Android SDKで正式サポートされました!) - Qiita
support packageに追加されたtesting-support-libを使ってAndroidのテストをJUnit4で書く - visible true


JUnit4 で Activity のテストを書いてみる

@@ -10,6 +10,8 @@ android {
         targetSdkVersion 21
         versionCode 1
         versionName "1.0"
+        // testing
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
     }
     buildTypes {
         release {
@@ -17,9 +19,16 @@ android {
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
         }
     }
+    packagingOptions {
+        exclude 'LICENSE.txt'
+    }
 }

 dependencies {
     compile fileTree(dir: 'libs', include: ['*.jar'])
     compile 'com.android.support:appcompat-v7:21.0.3'
+    // testing
+    compile 'com.android.support:support-annotations:21.0.3'
+    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0'
+    androidTestCompile 'com.android.support.test:testing-support-lib:0.1'
 }
package com.wada811.android.junit4;

import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.ActivityInstrumentationTestCase2;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.notNullValue;

@RunWith(AndroidJUnit4.class)
public class MainActivityTest extends ActivityInstrumentationTestCase2 {

    private MainActivity activity;

    public MainActivityTest(){
        super(MainActivity.class);
    }

    @Before
    public void setUp() throws Exception{
        super.setUp();
        injectInstrumentation(InstrumentationRegistry.getInstrumentation());
        activity = getActivity();
    }

    @After
    public void tearDown() throws Exception{
        super.tearDown();
    }

    @Test
    public void checkPrecondition(){
        assertThat(activity, notNullValue());
        assertThat(getInstrumentation(), notNullValue());
    }

}

参考

Getting Started - EspressoSetupInstructions - android-test-kit
AndroidJUnitRunnerUserGuide - android-test-kit

テストを実行する

./gradlew connectedAndroidTest コマンドを実行

端末もしくはエミュレータでテストを実行することができます。
しかし、何故か MainActivityTest.java は実行されませんでした。
JUnit3 なテストは実行されていますが、 JUnit4 な テストは実行されないみたいです。

プロジェクトエクスプローラで右クリックから Run...

ファイルを指定して実行すれば JUnit4 なテストも実行できます。


Edit Configurations... から Android Test を追加してクラスを指定して実行

クラス(ファイル)を指定して実行すれば JUnit4 なテストも実行できます。
All in Module や All in Package では JUnit4 なテストは実行されませんでした。


クラスを指定しなくても JUnit4 なテストを実行したい

これは偶然なのか考えられているのかわからないけど Jake 神の ActivityRule を使うと
直接クラスを指定しなくてもテストを実行できるようになります。
package com.wada811.android.junit4;

import android.support.test.runner.AndroidJUnit4;
import com.jakewharton.test.ActivityRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import static android.support.test.espresso.Espresso.*;
import static android.support.test.espresso.assertion.ViewAssertions.*;
import static android.support.test.espresso.matcher.ViewMatchers.*;
import static org.hamcrest.Matchers.*;

@RunWith(AndroidJUnit4.class)
public class MainActivityTestWithRule {

    @Rule
    public final ActivityRule activityRule = new ActivityRule<>(MainActivity.class);

    @Test
    public void checkPrecondition(){
        // MatcherAssert.assertThat(activityRule.instrumentation(), Matchers.notNullValue());
        assertThat(activityRule.instrumentation(), notNullValue());
    }

    @Test
    public void showHelloWorld(){
        // Espresso.onView(ViewMatchers.withId(R.id.textView)).check(ViewAssertions.matches(ViewMatchers.withText(R.string.hello_world)));
        onView(withId(R.id.textView)).check(matches(withText(R.string.hello_world)));
    }

}
Espresso 2.0 でテストを書いてみました。
クラス名を覚えるのは辛い感じなので static import でメソッド書くだけにしたい感じがあります。

ソースコードは wada811/Android-JUnit4 にあります。
Activity だけじゃなく他のテストについても増やしていきたいです。

参考

A JUnit @Rule which launches an activity when your test starts. Stop extending gross ActivityInstrumentationBarfCase2!
2014/12/30

[Android]テキストをクリップボードにコピーする

wada811/Android-Material-Design-Colorsサンプルアプリを作ったら
カラーコードをコピーしたいという要望があったので実装しました。

Honeycomb 未満と以上で方法が変わっていました。
クラス名は同じでパッケージが異なるだけだったので長ったらしくてちょっとアレです。

@SuppressWarnings("deprecation")
public void copyText(Context context, String text){
    if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB){
        android.text.ClipboardManager clipboard = (android.text.ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE);
        clipboard.setText(text);
    }else{
        android.content.ClipboardManager clipboard = (android.content.ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE);
        ClipData clip = ClipData.newPlainText("Copied Text", text);
        clipboard.setPrimaryClip(clip);
    }
}
Android: Copy to clipboard selected text from a TextView - Stack Overflow

[Android]タブレット対応 〜 Master / Detail パターンを実装する

みなさん、タブレット対応してますか?
僕はまだあんまりなので
とりあえず一番有名な Master / Detail パターンを実装してみることにしました。
Android Studio の Wizard に Master Detail パターンがあるので
コイツを見ておきたいと思います。

ということで Android Studio の Wizard で Master Detail パターンを生成してみると
結構わかりにくいことをしていて、
values-sw600dp/refs.xml で @layout/activity_item_list を参照すると
タブレットでは @layout/activity_item_twopane を参照されるように設定しています。
<item type="layout" name="activity_item_list">@layout/activity_item_twopane</item>
しかも、スマートフォンとタブレットの判別の仕方が微妙で
ItemListActivity.java で setContentView(R.layout.activity_item_list) すると
タブレットでは R.layout.activity_item_twopane が読み込まれて
そのレイアウトの中に R.id.item_detail_container な View があるかで
スマートフォンとタブレットのレイアウトを振り分けています。
setContentView(R.layout.activity_item_list);

if(findViewById(R.id.item_detail_container) != null){
    mTwoPane = true;
}

タブレットかどうかの判定と View の実装は分離しておきたいので
タブレットかどうかのフラグをリソースに定義することにします。
Yukiの枝折: Android:タブレットorスマートフォンのフラグをリソースに定義する
<resources>
    <bool name="isTablet">false</bool>
</resources>
<resources>
    <bool name="isTablet">true</bool>
</resources>

ItemListActivity.java ではこのフラグを参照して処理をわけます。
レイアウトもこのフラグで振り分けます。

public class ItemListActivity extends ActionBarActivity implements ItemListFragment.Callbacks{

    private boolean isTablet;

    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);

        isTablet = getResources().getBoolean(R.bool.isTablet);
        setContentView(isTablet ? R.layout.activity_item_twopane : R.layout.activity_item_list);

        if(isTablet){
            getSupportActionBar().setDisplayShowHomeEnabled(false);
            getSupportActionBar().setDisplayUseLogoEnabled(false);

            ItemListFragment itemListFragment = (ItemListFragment)getSupportFragmentManager().findFragmentById(R.id.item_list);
            itemListFragment.setActivateOnItemClick(true);
            onItemSelected(0);
        }
    }

    @Override
    public void onItemSelected(int position){
        if(isTablet){
            Bundle arguments = new Bundle();
            arguments.putInt(ItemDetailFragment.ARG_ITEM_ID, position);
            ItemDetailFragment fragment = new ItemDetailFragment();
            fragment.setArguments(arguments);
            getSupportFragmentManager().beginTransaction()
                .replace(R.id.item_detail_container, fragment)
                .commit();
        }else{
            Intent detailIntent = new Intent(this, ItemDetailActivity.class);
            detailIntent.putExtra(ItemDetailFragment.ARG_ITEM_ID, position);
            startActivity(detailIntent);
        }
    }
}
wada811/Android-Material-Design-Colorsサンプルアプリ
Android Studio の Wizard の Master Detail パターンを元に
上記の変更を加えたアプリになっているので参考になるかもしれません。
ソースコードはこちら↓
Android-Material-Design-Colors/sample at master · wada811/Android-Material-Design-Colors
2014/10/24

AndroidStudio で APK を特定のフォルダにコピーする Gradle の設定

AndroidStudio で APK のファイル名を変更する Gradle の設定 | DevAchieve
APK をリネームしましたが、
出力されるフォルダが build/outputs/apk/ なので
変更したいことがあるかと思います。
Gradle の Task を定義してあげれば
任意のフォルダにコピーする処理を実行することができます。

Gradle で Signed APK とProGuard 関連ファイルをコピーするタスクを設定する


Add move apk task and move proguard task · c7cdd90 · wada811/Android-Material-Design-Colors

applicationVariants.all { variant ->
    if (variant.buildType.name.equals("release")) {
        variant.outputs.each { output ->
            if (output.outputFile != null && output.outputFile.name.endsWith('.apk')) {
                // Rename APK
                def applicationId = defaultConfig.applicationId
                def versionCode = defaultConfig.versionCode
                def versionName = defaultConfig.versionName
                def date = new java.text.SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date())
                def newName = "${applicationId}_r${versionCode}_v${versionName}_${date}.apk"

                def publish = project.tasks.create("publishAll")

                // Move and Rename APK
                def task = project.tasks.create("publish${variant.name.capitalize()}Apk", Copy)
                task.from(output.outputFile)
                task.rename(output.outputFile.name, newName)
                task.into(file("${variant.name}/apk").getAbsolutePath())

                task.dependsOn variant.assemble
                publish.dependsOn task

                // Move ProGuard
                if (variant.buildType.runProguard) {
                    def copyTask = project.tasks.create("copy${variant.name.capitalize()}MappingText", Copy)
                    def buildTypeName = variant.buildType.name
                    copyTask.from(file("build/outputs/proguard/${buildTypeName}").path)
                    copyTask.into(file("${variant.name}/proguard").getAbsolutePath())

                    copyTask.dependsOn variant.assemble
                    task.dependsOn copyTask
                }
            }
        }
    }
}

実行

./gradlew publishAll
app/release/apk/ に Signed APK が、
app/release/proguard/ に ProGuard 関連ファイルがコピーされます。

参考

AndroidStudioでAPKを作ったあとに特定のディレクトリにAPKをコピーする - Qiita
2014/10/23

AndroidStudio で APK のファイル名を変更する Gradle の設定

Android Studio で Sigined APK を生成するには
ツールバーの [ Build > Generate Signed APK... ] から
ガイダンスに従えば生成できます。
しかし、GUI からではファイル名は app-release.apk などになるので
変更したい場合などは Gradle の設定が必要になります。

Gradle で Signed APK のファイル名を設定する

Generate signed APK has been named by program · bc41551 · wada811/Android-Material-Design-Colors

apply plugin: 'com.android.application'

android {
    compileSdkVersion 21
    buildToolsVersion "21.0.1"

    defaultConfig {
        applicationId "at.wada811.android.material.design.colors.sample"
        minSdkVersion 8
        targetSdkVersion 21
        versionCode 2
        versionName "1.1.0"
    }
    signingConfigs {
        release
    }
    buildTypes {
        release {
            runProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }

    applicationVariants.all { variant ->
        if (variant.buildType.name.equals("release")) {
            def file = variant.outputFile
            def applicationId = defaultConfig.applicationId
            def versionCode = defaultConfig.versionCode
            def versionName = defaultConfig.versionName
            def date = new java.text.SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date())
            def newName = "${applicationId}_r${versionCode}_v${versionName}_${date}.apk"
            variant.outputFile = new File(file.parent, newName)
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:21+'
    compile project(':library')
}

./gradlew assembleReleaseすると
at.wada811.android.material.design.colors.sample_r2_v1.1.0_20141022_173737.apk
のようなファイル名で app/build/outputs に生成されます。

参考

gradle - AndroidStudioでAPKのファイル名にバージョン番号などを入れる設定 - Qiita

追記: Android Gradle Plugin 0.13, Gradle 2.1 で outputFile が deprecated になっている

WARNING [Project: :sample] variant.getOutputFile() is deprecated. Call it on one of variant.getOutputs() instead. が表示されるので書き直しました。

applicationVariants.all { variant ->
    if (variant.buildType.name.equals("release")) {
        variant.outputs.each { output ->
            System.println("* output.outputFile.name : ${output.outputFile.name}")
            if (output.outputFile != null && output.outputFile.name.endsWith('.apk')) {
                // Rename APK
                def applicationId = defaultConfig.applicationId
                def versionCode = defaultConfig.versionCode
                def versionName = defaultConfig.versionName
                def date = new java.text.SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date())
                def newName = "${applicationId}_r${versionCode}_v${versionName}_${date}.apk"
                output.outputFile = new File(output.outputFile.parent, newName)
            }
        }
    }
}

outputs というから複数あるのかとおもいきや apk しかありませんでした。
無駄にネストが深くなる…。

参考

android - Gradle warning: variant.getOutputFile() and variant.setOutputFile() are deprecated - Stack Overflow
2014/10/22

AndroidStudio で APK の署名の設定を gradle.properties に記述する

Android Studio で Sigined APK を生成するには
ツールバーの [ Build > Generate Signed APK... ] から
ウィザードに従えば生成できます。
しかし、CUI から Signed APK を生成したいことがあるかと思います。
また、そのプロジェクトを public リポジトリで管理している場合に
署名に関する設定を公開しないようにするにはどうすればいいのかという問題もあります。

以下に示す方法では、証明に関する設定を gradle.properties に記述し、
.gitignore で gradle.properties を公開しないようにすることで
署名に関する情報を秘密にしておくおことが可能です。

Generate signed APK has been named by program · bc41551 · wada811/Android-Material-Design-Colors

apply plugin: 'com.android.application'

android {
    compileSdkVersion 21
    buildToolsVersion "21.0.1"

    defaultConfig {
        applicationId "at.wada811.android.material.design.colors.sample"
        minSdkVersion 8
        targetSdkVersion 21
        versionCode 2
        versionName "1.1.0"
    }
    signingConfigs {
        release
    }
    buildTypes {
        release {
            runProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }
}

...

if (project.hasProperty('storeFile')) {
    android.signingConfigs.release.storeFile = new File(System.getenv('HOME'), storeFile)
}
if (project.hasProperty('storePassword')) {
    android.signingConfigs.release.storePassword = storePassword
}
if (project.hasProperty('keyAlias')) {
    android.signingConfigs.release.keyAlias = keyAlias
}
if (project.hasProperty('keyPassword')) {
    android.signingConfigs.release.keyPassword = keyPassword
}

storeFile=/path/to/your.keystore
storePassword=keystorePass
keyAlias=appAlias
keyPassword=appPass

gradle.properties

new File(System.getenv('HOME'), storeFile) がキモで、
file(storeFile) にするとプロジェクトの相対パスで認識されるから
プロジェクトに keystore を含めるか、無理やり相対パスからたどるかになるんだけど
前者はプロジェクトごとに keystore を入れなければならなくて一元管理できないし、
後者は環境によってパスが変わりうるから微妙になります。
その点、new File(System.getenv('HOME'), storeFile) は $HOME からの絶対パスになるので統一しやすいです。

参考

AndroidStudio - Android Studio(Gradle)でapkファイルを作成する時にstorePassword/keyAlias/keyPasswordの指定方法をいくつか検証してみた。 - Qiita
2014/10/15

[Android]ActionBar のタイトルの文字色を変更する

ActionBar のタイトルの文字色を変えるメソッドはないので
あまり変えてやろうと思うこともないかと思いますが
気まぐれに文字色を変えたくなった時のためにメモしておきます。

Theme でActionBar のタイトルの文字色を変更する

普通の方法ですが、動的に変更することができません。
<resources>

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="actionBarStyle">@style/AppTheme.ActionBarStyle</item>
    </style>

    <style name="AppTheme.ActionBarStyle" parent="Widget.AppCompat.Light.ActionBar">
        <item name="titleTextStyle">@style/AppTheme.ActionBar.TitleTextStyle</item>
    </style>

    <style name="AppTheme.ActionBar.TitleTextStyle" parent="TextAppearance.AppCompat.Widget.ActionBar.Title">
        <item name="android:textColor">@color/red</item>
    </style>

</resources>
<resources>

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="android:actionBarStyle">@style/AppTheme.ActionBarStyle</item>
    </style>

    <style name="AppTheme.ActionBarStyle" parent="Widget.AppCompat.ActionBar">
        <item name="android:titleTextStyle">@style/AppTheme.ActionBar.TitleTextStyle</item>
    </style>

</resources>

Programmatically

String title = getResources().getString(R.string.title);
int titleColor = getResources().getColor(R.color.titleColor);
String htmlColor = String.format(Locale.US, "#%06X", (0xFFFFFF & Color.argb(0, Color.red(titleColor), Color.green(titleColor), Color.blue(titleColor))));
String titleHtml = "<font color=\"" + htmlColor +  "\">" + title + "</font>";
getSupportActionBar().setTitle(Html.fromHtml(titleHtml));

Html とかウケる。こんな方法があったとは。

参考
android - ActionBar text color - Stack Overflow
2014/09/20

[Android]Google Play アカウント所有者を変更する

Google Play アカウント所有者が個人Gmailアカウントに紐付いていて
ユーザーに開発用Gmailアカウントを追加していたのですが、
Google Play アカウント所有者じゃないとできないことがあったので
Google Play アカウント所有者を開発用Gmailアカウントに切り替える手続きをしました。

移行できれば普段使っている開発用Gmailアカウントで
Google Play Developer Publishing API などを使えるようになるので嬉しいです。


アカウント所有者 と ユーザー の違い

アカウント ユーザーの追加と権限の管理 - Android デベロッパー ヘルプ

タイプアクセス
アカウント所有者
デベロッパー コンソールにフルアクセスできる
新しいユーザーの招待、ユーザーのデベロッパー コンソールへのアクセスの取り消し、各ユーザーの権限の設定を行うことができる
有料アプリを販売するため、関連付けられた Google ウォレット Merchant アカウントを所有できる(アカウント所有者のみ)
ユーザー
デベロッパー コンソールにさまざまなレベルでアクセスできる
すべてまたは指定のアプリにアクセスできる
新しいユーザーの招待やユーザーの権限の編集はできない

Google Play アカウント所有者を変更する

手順は以下のとおり。

  • Google サポートチームに連絡し、アプリを別のアカウントに移行します。
  • アプリの移行の確認に返信し、既存のアカウントを閉鎖します。

アカウント情報の変更または更新 - Android デベロッパー ヘルプ


Google Play アカウントを移行する

普通だったら以下の対応だけでOK!

  • 新しい Google Play アカウントを登録する
  • 新しい Google Wallet アカウントを登録して紐付ける
  • アプリ移行リクエストを送る

アプリを移行する - Android デベロッパー ヘルプ
アプリ移行リクエスト - Android デベロッパー ヘルプ

アプリの収益を受け取るための Google Wallet のお支払い設定を忘れないようにしましょう!


デベロッパー アカウントの解約

2,3営業日待てば移行もされるし、
Google Wallet のデポジットも振り込まれてお支払い設定完了できるだろうから
すべてが終わったら移行元の Google Play アカウントを解約しましょう!

登録料の25ドルが返ってくるらしいです。
アカウント情報の変更または更新 - Android デベロッパー ヘルプ

2014/09/19

[Android]KitKatと外部記憶領域とREAD_EXTERNAL_STORAGEとWRITE_EXTERNAL_STRAGEとmaxSdkVersion

KitKat で外部記憶領域の読み書きに関するパーミッションに変更がありました。
また、READ_EXTERNAL_STORAGE パーミッションも正式対応となりました。

この変更についてまとめたいと思います。
まずは言葉の説明から。

外部記憶領域(primary external storage)

以前は外部記憶領域といえば SD カード(外部ストレージ、外部SDなどと呼ばれる)のことを指しましたが、
最近はストレージ内部の仮想的な記憶領域(内部ストレージ、内部SDなどと呼ばれる)のことを主に指します。
そのため、内部ストレージと呼ばれるのに外部記憶領域であり、
READ_EXTERNAL_STORAGE や WRITE_EXTERNAL_STRAGE が必要となるという少々混乱する状況でした。
(Environment#getExternalStorageDirectory()が内部SDを返すようになったり)
primary external storage などについては以下のサイトを読んでください。
External Storage Technical Information | Android Developers

KitKat での変更

KitKat からは EXTERNAL_STORAGE パーミッションの対象ディレクトリが変更となり、
外部記憶領域上のアプリケーションデータ領域はパーミッションが不要となりました。
アプリケーションデータ領域の外部については EXTERNAL_STORAGE パーミッションが必要となっています。

アプリケーションデータ領域

本体メモリのアプリケーションデータ領域(アプリ内領域などと呼ばれる)
/data/data/<パッケージ名>/
外部記憶領域上のアプリケーションデータ領域
/<外部記憶領域>/Android/data/<パッケージ名>

本題

READ_EXTERNAL_STORAGE と WRITE_EXTERNAL_STRAGE が必要なのは
どのディレクトリなのか、どのメソッドなのか調べました。

wada811/Android-StorageReadWriteChecker
BuildVariants と パーミッションREAD_EXTERNAL_STORAGEWRITE_EXTERNAL_STRAGE
PreKitKat
PreKitKatRead
PreKitKatWrite
PreKitKatReadWrite
KitKat
KitKatRead
KitKatWrite
KitKatReadWrite
KitKatWriteCompat○(maxSdkVersion="18")
KitKatReadWriteCompat○(maxSdkVersion="18")
データ:Android - KitKatと外部記憶領域とREAD_EXTERNAL_STORAGEとWRITE_EXTERNAL_STRAGEとmaxSdkVersion - Qiita

結果

PreKitKatKitKat の結果から
getExternalCacheDir と getExternalFilesDir で取得できる外部記憶領域上のアプリケーションデータ領域は
KitKat からパーミッションなしでも読み書き可能であることが確認できます。

READ_EXTERNAL_STORAGE が正式対応となったため
KitKat 以降で READ_EXTERNAL_STORAGE なしでは
Environment.getExternalStorageDirectory で取得できる外部記憶領域は
読み込みできないことが確認できます。(PreKitKatKitKat)
KitKatRead から READ_EXTERNAL_STORAGE があれば
外部記憶領域の読み込みができることが確認できますね。

本題の本題

さて、getExternal~Dir で取得できる外部記憶領域上のアプリケーションデータ領域は
KitKat からパーミッションなしでも読み書き可能ということが確認できましたが、
PreKitKat ではパーミッションが必要です。
KitKat では必要でないパーミッションを PreKitKat のために宣言すると
KitKat ユーザーに対して不要なパーミッションを取得することになります。
結局パーミッションは減らせないのかとなりそうですが、
KitKat で追加された Uses-Permission の maxSdkVersion 属性によって対応が可能です。

以下のように記述することで PreKitKat では WRITE_EXTERNAL_STORAGE を宣言し、KitKat ではパーミッションを宣言しないことができます。
<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="18" />
アプリ情報画面のパーミッションの欄を見ると PreKitKat では WRITE_EXTERNAL_STORAGE があり、
KitKat では WRITE_EXTERNAL_STORAGE はないことがわかります。
KitKatWriteCompat KitKatReadWriteCompat
PreKitKat
KitKat

結論

  • Environment への書き込みがあるなら WRITE_EXTERNAL_STORAGE が必要
  • getExternal~Dir への書き込みだけなら WRITE_EXTERNAL_STORAGE に maxSdkVersion="18" を付けておく
  • 外部記憶領域への読み込みがあるなら READ_EXTERNAL_STORAGE が必要

参考

External storage access | Android 4.4 APIs | Android Developers
Android:KitkatのREAD_EXTERNAL_STORAGEと外部記憶領域 | Taosoftware
WRITE_EXTERNAL_STRAGEと下位互換のためのmaxSdkVersion | Taosoftware
2014/09/12

[Android]ライブラリプロジェクト(aar)をMavenリポジトリとしてGitHubで配布する

Androider の皆さんこんにちは。
もう Eclipse から Android Studio への移行は済みましたか?
僕はまだ途中です。
俺々ライブラリが移行できないと Android Studio に移行できないので
今回はそのための手順をご紹介したいと思います。

環境

OS
Mac OS X 10.8.5
Android Studio
0.8.9

ライブラリプロジェクト(aar)とは

Android Studio 、というより Android Studio で導入された Gradle というビルドシステムでの
ライブラリプロジェクトの配布用バイナリ形式を aar (Android archive) と呼ぶらしいです。
.aar の拡張子を持つ ZIP ファイルで、以下が入っています。
  • /AndroidManifest.xml (必須)
  • /classes.jar (必須)
  • /res/ (必須)
  • /R.txt (必須)
  • /assets/ (オプション)
  • /libs/*.jar (オプション)
  • /jni//*.so (オプション)
  • /proguard.txt (オプション)
  • /lint.jar (オプション)
参考: AAR Format - Android Tools Project Site

配布編

まずは Android Studio でライブラリプロジェクトを作成します。詳しい方法については割愛。

試しに aar を作成するためリリースビルドするため、
上の画像の Terminal で ./gradlew assembleRelease を実行します。
以下のコマンドで aar が作成されているか確認します。
find . -name '*.aar'
./library/build/outputs/aar/library.aar

問題ないので build.gradle に Maven プラグインを使用して aar やら何やらを生成するタスクを追記します。
以下のハイライト部分です。
(余談だけど build.gradle って説明された時に初めは root の build.gradle なのか
Modules の build.gradle なのかわからなくて戸惑いました。だいたい Modules の方らしい。)
apply plugin: 'com.android.library'

android {
    compileSdkVersion 19
    buildToolsVersion "20.0.0"

    defaultConfig {
        minSdkVersion 10
        targetSdkVersion 19
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:20.0.0'
}

def repo = new File(rootDir, "repository")

apply plugin: 'maven'

uploadArchives {
    repositories {
        mavenDeployer {
            repository url: "file://${repo.absolutePath}"
            pom.version = '1.0.0'
            pom.groupId = 'at.wada811'
            pom.artifactId = 'android-dialog-fragments'
        }
    }
}
pom.version がライブラリのバージョン、
pom.groupIdが自分用の識別子(Java の package 名の最初の部分で良さそう)、
pom.artifactIdがライブラリの識別子、みたいです。
詳しくは 第52章 Mavenプラグイン を読んで下さい。

追記したら Android Studio の Terminal で ./gradlew uploadArchivesすると
repository ディレクトリに aar が生成されます。
tree repository
repository
└── at
    └── wada811
        └── android-dialog-fragments
            ├── 1.0.0
            │   ├── android-dialog-fragments-1.0.0.aar
            │   ├── android-dialog-fragments-1.0.0.aar.md5
            │   ├── android-dialog-fragments-1.0.0.aar.sha1
            │   ├── android-dialog-fragments-1.0.0.pom
            │   ├── android-dialog-fragments-1.0.0.pom.md5
            │   └── android-dialog-fragments-1.0.0.pom.sha1
            ├── maven-metadata.xml
            ├── maven-metadata.xml.md5
            └── maven-metadata.xml.sha1
この repository を GitHub に Push すれば配布側の手順としては完了です。

利用編

GitHub で配布されているライブラリプロジェクト(aar) を参照することで利用することができます。
プロジェクトは準備出来ているとして、app などの build.gradle に以下を記述します。
repositories {
    maven { url 'http://raw.github.com/wada811/Android-DialogFragments/master/repository/' }
}

dependencies {
    compile 'at.wada811:android-dialog-fragments:1.0.0'
}
全体は HelloGradle/build.gradle at master · wada811/HelloGradle を見て下さい。
読み込めると以下のように app/build/intermidiates/exported-aar/ 以下に追加されます。


これで Public な俺々ライブラリが手軽に配布、参照できて便利ですね!

参考

u1aryzの備忘録とか: githubをMavenリポジトリとしてAndroidライブラリプロジェクト(aar)をデプロイして使用する
2014/09/10

Android Studio で support library のソースコードをアタッチする

Android Studio でサポートライブラリのソースコードが読めなくて困ったので調べると
サポートライブラリのソースコードを別途ダウンロードして設定が必要でした。

参考
Android Studio - Attach Source - web系な備忘録

cd $ANDROID_SDK_HOME/extras/android
git clone https://android.googlesource.com/platform/frameworks/support/ support-src

FragmentActivity などを command + B で開いて
右上の Attach Source... をクリックして
$ANDROID_SDK_HOME/extras/android/support-src を指定してあげれば
サポートライブラリのソースコードを読めるようになります。
2014/07/06

[ShellScript]adb-screencap で Android 端末の画面をキャプチャしてPCに持ってくるコマンド

adbでスクリーンショットを撮るやつ - teshi04 / adb-screencap.sh
↑を実行するのに ./adb-screencap.sh とかするよりパスの通っているところに置いて
コマンドっぽく adb-screencap ってしたかったので書き換えてみた。
ついでに複数端末接続時に端末を指定してキャプチャできるようにしてみた。

man の書き方とか contribution の書き方とかライセンスとかどうすればいいのかわからないので
誰か良い感じにして下さい。
全然エラー制御していなくて、端末が認識されていないのに実行したり、
複数端末が認識されているのに端末を指定せずに実行した時はコマンドの分だけエラーが出ます。

入門UNIXシェルプログラミングを途中まで読んでいたので
制御文の書き方レベルしか知らなくて
初めてのシェルプログラミングだったので空文字チェックで地味に詰まりました。

if [ -z $SERIAL ] だと $SERIAL が null の場合
if [ -z ] と評価されて unary operator expected というエラーが出ます。
if [ -z "$SERIAL" ] のようにクォートで囲って
if [ -z "" ] と評価されるようにすれば
-z (Zero length) の引数にちゃんと空文字が渡されてエラーになりません。

あ、あとコメントの TODO にある通り、オプション引数の有無でコマンドを分けていて微妙なので
誰か良い方法を知っていたら教えて下さい。よろしくお願いします。
GitHub→ wada811/ADB-Tools

参考
シェルスクリプトを公開するとき, コマンド前にバックスラッシュをつけるべき - Life is very short
Bash - 今更ながら抑えておきたいシェルスクリプト用チートシート - Qiita
シェルスクリプトの条件評価ではまりやすいところ - ritchiekotzen's blog
シェルスクリプトで空文字列かどうか調べる。 - masa.edw the ハバネロブリーダー
2014/06/25

[Android]Bitmap のモザイク加工処理

DevCamera [カメラ/連射/無音/無音連射/ビデオ] - Google Play の Android アプリ
顔検出カメラを追加して写真を撮ったらモザイクがかかるようにしてみた。
完全にお遊びで顔検出 API を使って作ってみたのでモザイクくらいしかやることがなかった
あまり意味のないカメラです。

ピクセルを取得して周辺のピクセルの色平均をとってピクセルを戻すだけの処理です。

public static Bitmap mosaic(Bitmap bitmap){
    int dot = 16;
    int width = bitmap.getWidth();
    int height = bitmap.getHeight();
    int square = dot * dot;
    int[] pixels = new int[width * height];
    bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
    // ピクセルデータ分ループ
    for(int w = 0, widthEnd = width / dot; w < widthEnd; w++){
        for(int h = 0, heightEnd = height / dot; h < heightEnd; h++){
            // ドットの中の平均値を使う
            int r = 0;
            int g = 0;
            int b = 0;
            int moveX = w * dot;
            int moveY = h * dot;
            for(int dw = 0; dw < dot; dw++){
                for(int dh = 0; dh < dot; dh++){
                    int dotColor = pixels[moveX + dw + (moveY + dh) * width];
                    r += Color.red(dotColor);
                    g += Color.green(dotColor);
                    b += Color.blue(dotColor);
                }
            }
            r = r / square;
            g = g / square;
            b = b / square;
            for(int dw = 0; dw < dot; dw++){
                for(int dh = 0; dh < dot; dh++){
                    pixels[moveX + dw + (moveY + dh) * width] = Color.rgb(r, g, b);
                }
            }
        }
    }
    bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
    return bitmap;
}
Bitmap#copyPixelsToBuffer / Bitmap#copyPixelsFromBuffer の方が速いらしいので書き換えてみた。 本当にこの書き方であっているか自信がない。そしてループ処理が遅すぎて微々たる差しかないように感じた。
public static Bitmap mosaicBuffer(Bitmap bitmap){
    int dot = 16;
    int width = bitmap.getWidth();
    int height = bitmap.getHeight();
    int square = dot * dot;
    int[] pixels = new int[width * height];
    IntBuffer buffer = IntBuffer.wrap(pixels);
    buffer.position(0);
    bitmap.copyPixelsToBuffer(buffer);
    // ピクセルデータ分ループ
    for(int w = 0, widthEnd = width / dot; w < widthEnd; w++){
        for(int h = 0, heightEnd = height / dot; h < heightEnd; h++){
            // ドットの中の平均値を使う
            int r = 0;
            int g = 0;
            int b = 0;
            int moveX = w * dot;
            int moveY = h * dot;
            for(int dw = 0; dw < dot; dw++){
                for(int dh = 0; dh < dot; dh++){
                    int dotColor = pixels[moveX + dw + (moveY + dh) * width];
                    r += Color.red(dotColor);
                    g += Color.green(dotColor);
                    b += Color.blue(dotColor);
                }
            }
            r = r / square;
            g = g / square;
            b = b / square;
            for(int dw = 0; dw < dot; dw++){
                for(int dh = 0; dh < dot; dh++){
                    pixels[moveX + dw + (moveY + dh) * width] = Color.rgb(r, g, b);
                }
            }
        }
    }
    buffer.position(0);
    bitmap.copyPixelsFromBuffer(buffer);
    return bitmap;
}
遅すぎるので[Android]Camera#addCallbackBuffer と Camera#setPreviewCallbackWithBuffer を使うで 書いたように RenderScript で実装するか OpenCV for Android を使うしかないのかもしれない。

参考

Bitmap.setPixel()をsetPixels()に変えたら3倍速くなったよ | Android Techfirm Lab 楓 software: getPixels と copyPixelsToBuffer
2014/06/24

[Android]Camera#addCallbackBuffer と Camera#setPreviewCallbackWithBuffer を使う

Camera#addCallbackBuffer(byte[])
Camera#setPreviewCallbackWithBuffer(Camera.PreviewCallback)ですが、
使い方がわかりにくかったのでメモしておきます。

まず準備として以下の記述を消します。
自分で Canvas に描画するので必要がないのですね。
mCamera.setPreviewDisplay(holder);

そして全体は以下のような感じで実装します。
private Camera mCamera;
private int mPictureOrientation; // デバイスの向きから計算した写真の向き
private Camera.Size mPreviewSize; // Preview サイズ
private Camera.Size mScaledPreviewSize; // Preview サイズを縦横比を維持しつつ画面サイズに拡大したサイズ
private byte[] mCallbackBuffer;

private void startPreview(){
    int size = mPreviewSize.width * mPreviewSize.height * ImageFormat.getBitsPerPixel(mCamera.getParameters().getPreviewFormat()) / 8;
    mCallbackBuffer = new byte[size];
    mCamera.addCallbackBuffer(mCallbackBuffer);
    mCamera.setPreviewCallbackWithBuffer(this);
}

private void stopPreview(){
    mCamera.setPreviewCallbackWithBuffer(null);
}

@Override
public void onPreviewFrame(byte[] data, Camera camera){
    // 画像変換処理
    Bitmap bitmap = BitmapUtils.createBitmapFromYuv(mContext, data, mPreviewSize.width, mPreviewSize.height);
    bitmap = BitmapUtils.rotate(bitmap, mPictureOrientation);
    bitmap = BitmapUtils.resize(bitmap, mScaledPreviewSize.width, mScaledPreviewSize.height);
    // 描画処理
    Canvas canvas = mHolder.lockCanvas();
    if(canvas != null){
        canvas.drawBitmap(bitmap, 0, 0, null);
        mHolder.unlockCanvasAndPost(canvas);
    }
    // 次のフレームのコールバックの設定
    mCamera.addCallbackBuffer(mCallbackBuffer);
}
BitmapUtils.createBitmapFromYuv などは wada811/AndroidLibrary@wada811からです。
本当は Bitmap を毎フレーム生成するのは嫌なんですが
YUV データを Bitmap#setPixels を通して Bitmap に変換する方法がよくわからず…。
JavaFilter#decodeYUV420SP · youten/YUV420SP を使えばいけるかもしれない。
youten さんが書かれている通り、速度は出ないので RenderScript を使いたいところ。

やってみた

private Camera mCamera;
private int mPictureOrientation; // デバイスの向きから計算した写真の向き
private Camera.Size mPreviewSize; // Preview サイズ
private Camera.Size mScaledPreviewSize; // Preview サイズを縦横比を維持しつつ画面サイズに拡大したサイズ
private byte[] mCallbackBuffer;
private int[] mRgbDatas;
private Bitmap mPreviewBitmap;

private void startPreview(){
    mRgbDatas = new int[mPreviewSize.width * mPreviewSize.height];
    mPreviewBitmap = BitmapUtils.createNewBitmap(mPreviewSize.width, mPreviewSize.height);
    int size = mPreviewSize.width * mPreviewSize.height * ImageFormat.getBitsPerPixel(mCamera.getParameters().getPreviewFormat()) / 8;
    mCallbackBuffer = new byte[size];
    mCamera.addCallbackBuffer(mCallbackBuffer);
    mCamera.setPreviewCallbackWithBuffer(this);
}

private void stopPreview(){
    mCamera.setPreviewCallbackWithBuffer(null);
}

@Override
public void onPreviewFrame(byte[] data, Camera camera){
    // 画像変換処理
    BitmapUtils.decodeYUV420SP(mRgbDatas, data, mPreviewSize.width, mPreviewSize.height);
    mPreviewBitmap.setPixels(mRgbDatas, 0, mPreviewSize.width, 0, 0, mPreviewSize.width, mPreviewSize.height);
    Bitmap bitmap = BitmapUtils.rotate(mPreviewBitmap, mPictureOrientation);
    bitmap = BitmapUtils.resize(bitmap, mScaledPreviewSize.width, mScaledPreviewSize.height);
    // 描画処理
    Canvas canvas = mHolder.lockCanvas();
    if(canvas != null){
        canvas.drawBitmap(bitmap, 0, 0, null);
        mHolder.unlockCanvasAndPost(canvas);
    }
    // 次のフレームのコールバックの設定
    mCamera.addCallbackBuffer(mCallbackBuffer);
}
BitmapUtils#decodeYUV420SP も遅かったですが、
BitmapUtils#rotate と BitmapUtils#resize で Bitmap の生成をやってしまっているので更に遅かったです。

参考

#13 やってみよう画像処理『カメラのbyte列をいじる』(Androidで) | tech.kayac.com - KAYAC engineers' blog
2014/05/22

Android Studio のエディタのタブを切り替えるショートカットキー

Android Studio カスタマイズシリーズ第二弾。
Android Studio のエディタのタブを次のタブ、前のタブに切り替える
ショートカットキーを Sublime Text 2 と一致させたいと思います。

[Preference > Keymap] を開いて Tab で検索。

Select Next Tab: command + shift + ]
Select Previous Tab: command + shift + [

上記のように設定されているが、実際の動きはそうなっていません。

なぜなのか

Keymap shows characters for keys only for english keyboard layout correctly : IDEA-63779
Android Studio のベースである IntelliJ IDEA のバグで Keymap が
US キーボードの配置に依存しているようで
JP キーボードでは記号系の紐付きがおかしくなっているからのようです。
エディタでの入力はタイプしたものが見たままそのまま表示されますが、
Keymap のショートカットキー登録の部分だけが入力したものと異なるキーが表示されます。

[ を入力したら ] と、] を入力したら \ と表示されます。

どうすればいいのか

見た目上は異なるキーですが、結局入力したキーで反応するので

Select Next Tab: command + shift + ]
Select Previous Tab: command + shift + [

上記のように入力して以下のように表示されていれば良いです。

Select Next Tab: command + shift + [
Select Previous Tab: command + shift + \

このバグは混乱するので直して欲しいです。かなり放置されているので期待は薄そうですが…。
2014/05/21

Android Studioでカーソルをワード区切り単位で移動する

Android Studio が使いにくいです。
カーソル移動もままなりません。
これが IDE の移行コストか…と思いながら Keymap を眺める日々です。

さて、愚痴はこのへんにして Android Studio を調教していきましょう。

Android Studio のデフォルトの挙動では alt + , alt +
ワード単位で移動してしまいます。

これ↑をこう↓したいわけです。


どうすればいいのか?

[Preference > Keymap] で 検索バーに [ Move Caret Word Different ] を入力すると
以下の項目が見つかるかと思います。(検索バーの右のボタンを押せばショートカットキーからも検索できます)
Move Caret to Next Word in Different "Camel|Humps" Mode
Move Caret to Previous Word in Different "Camel|Humps" Mode
この項目で右クリックして [ Add Keyboard Shortcut ] を選択後、
割り当てたいショートカットキーを入力します。
ショートカットキーが衝突した場合は既存のショートカット設定を削除して上書きすることができます。

こうして僕は alt + , alt + を設定して幸せになれました。

上記の動きで文字列を選択したい

以下の項目に alt + shift + , alt + shift + を割り当てました。

Move Caret to Next Word with Selection in Different "Camel|Humps" Mode
Move Caret to Previous Word with Selection in Different "Camel|Humps" Mode

2014/05/20

Android Studioのカラーテーマを変更/追加する

Android Studio といえばクールなダークテーマですよね?
ただでさえ新しいビルドシステム Gradle が導入され、
Android Studio で開発 イコール カッコイイ なのに
ダークテーマだなんてかっこ良すぎると思います。

羨ましいので Android Studio を導入してみたところ
別に普通のライトグレーな普通にテーマでした。
あれ?と思いつつ、調べたところ
Android Studio のイメージのクールなダークテーマは
[Preference > Appearance] の Theme で Darcula を設定すれば適用されます。

Appearance を Darcula すると自動的に エディタのカラーテーマも Darcula になるようです。
オレンジ系のカラーテーマはそんなに好きじゃなかったので
Themes for InteliJ IDEA, PhpStorm, PyCharm, RubyMine, WebStorm and AppCode. から
Monokai Sublime Text 3 というカラーテーマをダウンロードしてきました。
Android Studio の [File > Import Settings...] から読み込めばカラーテーマを追加できます。
エディタのカラーテーマは [Preference > Editor > Colors & Fonts] から変更できます。

なんでこれくらいのこと Eclipse でもやってなかったんだろうと思ったら
Eclipse はエディタ部分は変更できても Appearance が変更できないからやめたようです。
like Darcula - Kazzzの日記
Eclipse より Android Studio の方がイケてますね。完全に移行できるよう頑張ります。


参考

第3回 はじめましてのAndroidアプリケーション:Android Studio最速入門~効率的にコーディングするための使い方|gihyo.jp … 技術評論社
How to change or add theme to Android Studio? - Stack Overflow
Color Themes & Fonts
2014/05/17

MacでAndroid Studioが起動できない

JDK6 が入っていない

Brewfile + Homebrew + Homebrew-caskで Mac の環境構築をする | DevAchieve
Brewfile に以下のように記述しているから問題ないはず…!
cask install --appdir=/Applications java


と思ったら Android Studio をダブルクリックをしても反応がない…屍のようだ…

Android Studio.app/Contents/MacOS/studio を直接実行してみると
以下の様なエラーメッセージが出ていました。
JavaVM FATAL: Failed to load the jvm library.

どうやら Mac 版 Android Studio は JDK6 が必要なようです。
参考: Google Play Services SDK のセットアップ(Android Studio) - Qiita

以下のように記述して brew bundle ~/Brewfile を実行したら起動できました。
tap caskroom/versions

# JDK6/JDK7 for Android Studio
cask install --appdir=/Applications java7
cask install --appdir=/Applications java6
2014/04/27

[Android]AppWidgetを作成する

以下の記事を読めば AppWidget を作成する方法がわかります。ココはポインタ。
App Widgets | Android Developers
Widgets | Android Developers
App Widget Design Guidelines | Android Developers
AppWidgetManager | Android Developers
AppWidgetProvider | Android Developers
AppWidgetProviderInfo | Android Developers
RemoteViews | Android Developers

AndroidManifect.xml に AppWidgetProvider を宣言する

<receiver android:name="ExampleAppWidgetProvider" >
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data
        android:name="android.appwidget.provider"
        android:resource="@xml/appwidgetinfo" />
</receiver>

AppWidget のメタデータを設定する

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/appwidget"
    android:minHeight="40dp"
    android:minWidth="40dp"
    android:updatePeriodMillis="0" >
</appwidget-provider>
最低限必要なのはレイアウト指定とサイズ指定と更新間隔指定です。
サイズ指定は以下を参考に指定します。
App Widget Design Guidelines | Android Developers
# of Cells
(Columns or Rows)
Available Size (dp)
(minWidth or minHeight)
140dp
2110dp
3180dp
4250dp
n70 × n − 30
更新間隔は最短で30分です。
30分 = 30 * 60 * 1000 = 1800000 以下の値を設定しても 1800000 として処理されます。
デバイスがスリープ状態でもアウェイクしてしまうため頻度の高い更新をする場合は
updatePeriodMillis に 0 を指定し、 ELAPSED_REALTIME か RTC のどちらかを指定した AlarmManager で
PendingIntent を飛ばして BroadcastReciever から AppWidget を更新する方法で行うべきです。

AppWidgetProvider を作成する

public class ExampleAppWidgetProvider extends AppWidgetProvider {

    /**
     * 最初の1個目のAppWidgetが設置された時に呼ばれる
     */
    @Override
    public void onEnabled(Context context){
        super.onEnabled(context);
    }

    /**
     * AppWidget が設置された時/更新期間が来た時に呼ばれる
     */
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds){
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        for(int appWidgetId : appWidgetIds){
            updateAppWidget(context, appWidgetManager, appWidgetId);
        }
    }

    /**
     * AppWidget が削除された時に呼ばれる
     */
    @Override
    public void onDeleted(Context context, int[] appWidgetIds){
        super.onDeleted(context, appWidgetIds);
    }

    /**
     * 最後の AppWidget が削除された時に呼ばれる
     */
    @Override
    public void onDisabled(Context context){
        super.onDisabled(context);
    }

    /**
     * 上記のコールバックに振り分けるメソッド
     */
    @Override
    public void onReceive(Context context, Intent intent){
        super.onReceive(context, intent);
    }

    /**
     * AppWidget をリサイズした時に呼ばれる
     */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions){
        super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
        updateAppWidget(context, appWidgetManager, appWidgetId);
    }

}
ただの BroadcastReceiver の拡張なので時間のかかる処理を行うと ANR が発生するので
Service を開始して Service で時間のかかる処理を行った方が良いです。

View を設定する

public static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId){
    RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget);
    // AppWidgetManager 
    appWidgetManager.updateAppWidget(appWidgetId, views);
}
Creating the App Widget Layout | App Widgets | Android Developersを参考に
RemoteViews で使える View を使って AppWidget を作りましょう!
2014/04/25

[Android]画面サイズのダイアログを表示する

Days Counterで画面サイズのダイアログ Activity を表示したかったのですが
テーマなどでは上手いこと設定できなかったので調べてみたら
以下のようにプログラムから指定するようです。
WindowManager.LayoutParams params = getWindow().getAttributes();
params.width = LayoutParams.MATCH_PARENT;
params.height = LayoutParams.MATCH_PARENT;
getWindow().setAttributes(params);

タグ(RSS)