備忘録 〜プログラミング〜

プログラミングに関する事をつらつらと、、

adb shell am startでアプリを起動したときにQueryが全て取得出来ない問題

まず、URLからアプリが立ち上がるようにAndroidManifest.xmlに必要事項を追加

<!-- スキーム起動 -->
<activity
  android:name="com.hoge.app.IntentReceiverActivity"
  android:screenOrientation="portrait" >
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data
      android:host="www.hoge.com"
      android:pathPrefix="/app/-/intent"
      android:scheme="http" />
  </intent-filter>
</activity>

これで、http://www.hoge.com/app/-/intentにアクセスしたら、該当アプリ(com.hoge.app.IntentReceiverActivityを実装したアプリ)が選択出来るようになります。
IntentReceiverActivityの実装内容は、こちら

public class IntentReceiverActivity extends Activity{
  @Override
  protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    Intent intent = getIntent();
    if(intent != null){
      //ここでhttp://www.hoge.com/app/-/intentを取得
      Uri uri = intent.getData();
      //1つめのクエリをLogに出力
      Log.d("sample", "query1 is : " + uri.getQueryParameter("query1"));
      //2つめのクエリをLogに出力
      Log.d("sample", "query2 is : " + uri.getQueryParameter("query2"));
    }
  }
}

最後にクエリ取得テストのために使用するコマンドがこちら

$ adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d http://www.hoge.com/app/-/intent?query1=value1&query2=value2

これで実行してログを見るとこんな感じに

query1 is value1
query2 is null

query2もあるはずなのにnullになります。
解決方法は、「&」を使うのではなく「%26」を使う事。

$ adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d http://www.hoge.com/app/-/intent?query1=value1%26query2=value2

これで正常にクエリの取得が出来るようになります。
もしくは「&」の前にバックスラッシュを「\&」
こちらの場合だと、URLを「" "」で囲う必要があります。

$ adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "http://www.hoge.com/app/-/intent?query1=value1\&query2=value2"

adb shellからテストするときは要注意ですね。

https://code.google.com/p/android/issues/detail?id=76026
stackoverflow.com

APIからのResponseがJson形式だった場合にMap<String, String>でパースする方法

表題の通りAPIからのResponseがJSON形式だった場合にMap形式でパースがしたくなり調べてみたのでメモしておきます。
API接続とJSONのパースで使用したライブラリは以下の通りとなります。
API接続: Volley
JSONのパース: GSON

実装はこんな感じになります。

import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;

import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.Map;

public class SampleEntity implements Serializable{
  private static final long serialVersionUID = 7535272758660084740L;
  public Data data;
  public class Data{
    @SerializedName("hoge")
    private String hogeJson;

    public Map<String, String> getHoge(){
      Gson gson = new Gson();
      Type type = new TypeToken<Map<String, String>>(){}.getType();
      return gson.fromJson(hogeJson, type);
    }
  }
}

簡単なサンプルになりますが、こんな感じでJSONをMapに変換する事が出来ました〜
参考URLstackoverflow.com

Androidアプリでキャプチャーをされたくないときにする方法

Android 4.4からデバイス画面を直接キャプチャー出来るようになり、コンテンツの保護対応をする必要が出ました。
DeNAが最近リリースしたMirrativというアプリでキャプチャーされないようにしたい場合など有効なtipsになるかと思います。

1: キャプチャーしたくないActivityにFLAG_SECUREを設定する方法(SamppleActivity.java)

@Override 
public onCreate(Bundle savedInstanceState){
  super.onCreate(savedInstanceState);
  //FLAG_SECUREの設定
  getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,
        WindowManager.LayoutParams.FLAG_SECURE);
}

2: キャプチャーしたくないFragmentにFLAG_SECUREを設定する方法(SampleFragment.java)

WindowManager mWindowManager;
FrameLayout mLayoutView;
WindowManager.LayoutParams mParams;

@Override
public onCreate(Bundle savedInstanceState){
  super.onCreate(savedInstanceState);
  mParams = new WindowManager.LayoutParams(
          WindowManager.LayoutParams.TYPE_SYSTEM_OVERRAY,
          WindowManager.LayoutParams.FLAG_SECURE,
          PixelFormat.TRANSLUCENT);
  mWindowManager = (WindowManager) getActivity().getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
  mLayoutView = new FrameLayout(getActivity);
  mWindowManager.addView(mLayoutView, mParams);
}

@Override
public void onHiddenChanged(boolean hidden){
  super.onHIddenChanged(hidden);
  if(hidden){
    mWindowManager.removeView(mLayoutView);
  } else {
    mWindowManager.addView(mLayoutView, mParams);
  }
}

3: キャプチャーしたくないDialogFragmentにFLAG_SECUREを設定する方法(SampleDialogFragment.java)

@Override
public Dialog onCreateDialog(Bundle savedInstanceState){
  Dialog dialog = new Dialog(getActivity(), R.style.sample_dialog);
  setCancelable(true);
  dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,
          WindowManager.LayoutParam.FLAG_SECURE);
  return dialog;
}

4: キャプチャーしたくないSurfaceViewにsetSecure(boolean isSecure)で設定する方法(SampleSurfaceView.java)

public SampleSurfaceView(){
  if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
    setSecure(true);
  }
}

実装当初は、ActivityのみにFLAG_SECUREを設定していたので、Dialogでは表示されてしまったり、SurfaceViewを使用して動画を再生している部分で端末が横向きになったときにキャプチャーされてしまう問題などが色々と出てしまったのですが、ほぼほぼ上記の対応で大丈夫でした。

参考URL:
【ActivityにFLAG_SECURE】
Y.A.M の 雑記帳: Android アプリからスクリーンキャプチャを無効にする方法

【FlagmentにFLAG_SECURE】dev.classmethod.jp

【DialogにFLAG_SECURE】
http://my.fit.edu/~vkepuska/ece5570/adt-bundle-windows-x86_64/sdk/samples/android-17/ApiDemos/src/com/example/android/apis/app/SecureDialogActivity.java

【SurfaceViewにsetSecure】
SurfaceView | Android Developers

setTextSizeにgetDimensionの値を入れても意図したサイズになってくれない問題

コード上でテキストのサイズを指定する必要が出てきたので、このようにテキストサイズを指定してみました。

mTitle.setTextSize(getResource().getDimention(R.dimen.text_size_midium));

ちなみに、R.dimen.text_size_midiumは、dimens.xmlファイル内にこのように設定しています。

<dimen name="text_size_medium">18sp</dimen>

setTextで設定される値は、こちらの記事( http://y-anz-m.blogspot.jp/2012/02/androidtextview-settextsize-sp.html )によるとspとの事なので正常な値になるかなと思ったのですが、いざ実行してみると、何故か意図したサイズよりも大きい、、
getDimentionではなく、getDimensionPixelSizeを使用してみたのですが同じ。
そもそも、getDimentionもgetDimensionPixelSizeも返ってくる値は同じで、戻り値がfloatかintかの違いがあるのみのようでした。
では、どうするかというのが、こちら

mTitle.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResource().getDimention(R.dimen.text_size_midium));

TypedValue.COMPLEX_UNIT_PXを指定してデフォルト値をPXに変更しています。
SPで設定した文字は、ユーザーのフォントサイズによって変化してしまうので、狙った画面レイアウトが出来ないようですね。
こちらのサイト( https://akira-watson.com/android/dp_sp.html )が分かりやすかったです。
SPで指定するのは推奨されていないようですが、一旦この方法で解決しましたー
px、dp、sp、、色々と単位があってややこしいですな

【参考】stackoverflow.com

タブレットのみレイアウトを変更する方法

表題の通り、タブレットのみレイアウトや画像を変えたいときした事をメモしておきます。
レイアウト

res/layout-sw600dp

※参考devlog.au.kddi.com

画像

res/drawable-xlarge-mdpi/ (10インチ)
res/drawable-large (7インチ)

※参考stackoverflow.com

SwitchPreferenceでenable、disableを設定したときに文字色を変更する方法

SwitchPreference使用時にenable、disableでtitleの色を変更したくなり、調べてみました。
まず、drawableフォルダにテキストカラーのmycolor.xmlを作成

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <!-- disabled -->
  <item android:state_enabled="false" android:color="#868686" />
  <!-- enabled -->
  <item android:color="#000000"/>
</selector>

次に、valuesフォルダ内にthemes.xmlを作成。

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <style name="my_theme" parent="@android:style/Theme.DeviceDefault">
    <item name="android:background">#f0f0f0</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:textColor">@drawable/mycolor</item>
  </style>
</resources>

作成したテーマをPreferenceActivityに適応( AndroidManifest )

<!-- 通知設定画面 -->
<activity
  android:name="MyNotificationActivity"
  android:screenOrientation="portrait"
  android:launchMode="singleTask"
  android:theme="@style/my_theme" >
</activity>

これで大丈夫でした。
今まではenable、disableの文字色をコードで書いていたのですが、xmlで書くと楽になりますね。
ちなみに、SwitchPreferenceですが、Android5.+では正常に動作しない事があるようです。d.hatena.ne.jp
stackoverflow.com

API 4+ (Android 1.6+)でAsyncTaskのexecuteメソッドを使用しても並列処理にならない現象

動画をダウンロード出来るアプリ開発時に、AsyncTaskを使用して並列処理をしようと思ったのですが、HoneyCombo(API 11)以上の端末では2つめ以降のtaskが開始されないという現象が起きました。
問題は、executeメソッドで、HoneyCombo ( API 11 )以上はexecuteOnExecutorを使用するようです。
サンプルはこちら

@TargetApi(Build.VERSION_CODES.HONEYCOMB) // API 11
void startMyTask(AsyncTask asyncTask) {
  if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
    asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
  else
    asyncTask.execute(params);
}

これで無事に並列処理を行う事が出来ました〜stackoverflow.com