Flex、AIR、Java、Androidなど

2月24日 2009

【Android】端末を振ってタスクを切り替えるアプリケーションを作ってみました

Posted by: tachibana In: Android| プログラミング

先日から作っていた端末を振ることでタスクの切り替えを行うアプリケーション、一応動くようになったのでソース貼っておきます。

※センサーの実装部分はほとんどeggさまに教えてもらった実装そのままとコピペです。いつもありがとうございます。

パッケージはこんな感じ。

class

ShakeAgent.javaはActivityを継承、ShakeDetectorはServiceを継承、TriggerOnBootはBroadcastReceiverを継承し、デバイスの起動時に呼ばれるクラスです。

基本的にShakeAgentの起動時、もしくは端末の起動時にShakeDetectorがstartSeviceされ、端末の非ロック時にはServiceがバックグラウンドで振動を監視し、振動を検知したらタスクを切り替えます。

一応端末の振りの強さで処理を分岐させる関数も残してありますが、とりあえず現在は強く振っても弱く振ってもタスクの切り替えを行うよう記述しています。

パッケージとソースを見てもらえれば分かるかと思うのですが、なるべく機能の追加をしやすいようにしています。例えば「強く振ったときに電話をかける画面を開きたい」など振ったときに別の処理を行いたい場合はworkerパッケージ内に適当なクラスを追加し、ShakeDetectorの実装を変えてやれば簡単にできるかと思います。

TaskSwitcherクラスは前回振られたときからの時間を測定し、短いスパンだったらタスクリストの途中のActivityを呼ぶようにしているのでShakeDetectorのコンストラクタで初期化していますが、場合によってはローカルでも、staticでもいいかもしれません。

昨日悩んでいたSecurityExceptionについては、

E/AndroidRuntime( 2269): java.lang.SecurityException: Permission Denial: starting Intent { action=android.intent.action.MAIN categories={android.intent.category.LAUNCHER} flags=0x10000000 comp={com.android.calendar/com.android.calendar.DayActivity} } from ProcessRecord{43616330 2269:biz.stachibana.ShakeAgent/10067} (pid=2269, uid=10067) requires null

というような感じでそのアプリケーションが最初に起動するActivityを取れていないのが原因ぽかったので色々やってみて、

List<ActivityManager.RunningTaskInfo> runningTasks = new ArrayList<ActivityManager.RunningTaskInfo>();
・
・
・
intent.setComponent(runningTasks.get(currentIndex).baseActivity);

ではなく、

List<ActivityManager.RecentTaskInfo> runningTasks = new ArrayList<ActivityManager.RecentTaskInfo>();
・
・
・
intent.setComponent(runningTasks.get(currentIndex).baseIntent.getComponent());

とやってやることで解決しました。

apkは以下からダウンロードできます。

※アプリケーション、プログラムを利用した事によるいかなる損害への一切の責任を負いません。ご利用は自己責任にてお願いいたします。

ShakeAgent_alpha_signed.apk

何かあればコメントにてお願いします。

では、長いですが以下に全てのソースを貼っておきます。汚いと思いますので是非つっこみをお願いしますm(_ _)m


ShakeAgent.java

package biz.stachibana.ShakeAgent;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.util.Linkify;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;

public class ShakeAgent extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        LinearLayout layout = new LinearLayout(this);
        layout.setOrientation(LinearLayout.VERTICAL);
        layout.setPadding(30, 20, 30, 20);

        TextView info = new TextView(this);
        info.setText(R.string.app_info);
        info.setPadding(0, 0, 0, 20);
        layout.addView(info, new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.FILL_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT));

        TextView urlTB = new TextView(this);
        urlTB.setAutoLinkMask(Linkify.ALL);
        urlTB.setText(R.string.app_url);
        urlTB.setPadding(0, 0, 0, 20);
        layout.addView(urlTB, new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.FILL_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT));

        LinearLayout buttonContainer = new LinearLayout(this);
        buttonContainer.setOrientation(LinearLayout.HORIZONTAL);
        buttonContainer.setGravity(Gravity.CENTER_HORIZONTAL);

        Button btn1 = new Button(this);
        btn1.setText("start service");
        btn1.setOnClickListener(mStartListener);
        buttonContainer.addView(btn1, new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.WRAP_CONTENT,
                LinearLayout.LayoutParams.WRAP_CONTENT));

        Button btn2 = new Button(this);
        btn2.setText("stop service");
        btn2.setOnClickListener(mStopListener);
        buttonContainer.addView(btn2, new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.WRAP_CONTENT,
                LinearLayout.LayoutParams.WRAP_CONTENT));

        layout.addView(buttonContainer, new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.FILL_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT));

        setContentView(layout);

        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent = new Intent(ShakeAgent.this, ShakeDetector.class);
        startService(intent);
    };

    private OnClickListener mStartListener = new OnClickListener() {
        public void onClick(View v) {
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent = new Intent(ShakeAgent.this, ShakeDetector.class);
            startService(intent);
        }
    };

    private OnClickListener mStopListener = new OnClickListener() {
        public void onClick(View v) {
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent = new Intent(ShakeAgent.this, ShakeDetector.class);
            stopService(intent);
        }
    };
}

ShakeDetector.java

package biz.stachibana.ShakeAgent;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;

import android.app.Service;
import android.content.Intent;
import android.hardware.SensorListener;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.IBinder;
import android.widget.Toast;
import biz.stachibana.ShakeAgent.worker.TaskSwitcher;

public class ShakeDetector extends Service implements SensorListener {

    SensorManager sensorManager;
    static DecimalFormat format;
    static {
        format = new DecimalFormat();
        format.applyLocalizedPattern("#0.000");
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    TaskSwitcher taskSwitcher;

    @Override
    public void onCreate() {
        Toast.makeText(this, "Shake Agent started", Toast.LENGTH_LONG).show();
        super.onCreate();
        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        sensorManager.registerListener(this,
                SensorManager.SENSOR_ACCELEROMETER |
                SensorManager.SENSOR_ORIENTATION,
                SensorManager.SENSOR_DELAY_FASTEST);

        taskSwitcher = new TaskSwitcher(this);
    }

    @Override
    public void onDestroy()
    {
        Toast.makeText(this, "Stopped.", Toast.LENGTH_LONG).show();
        sensorManager.unregisterListener(this);
        super.onDestroy();
    }

    private float[] currentOrientationValues = {0.0f, 0.0f, 0.0f};
    private float[] currentAccelerationValues = {0.0f, 0.0f, 0.0f};

    boolean waitFlag = false;
    ArrayList<Float> valueArray = new ArrayList<Float>();
    Handler processHandler = new Handler();
    Runnable processRunnable = new Runnable(){
        public void run() {
        waitFlag = false;
        executeShake();
    }};

    public void onSensorChanged(int sensor, float[] values) {
        switch(sensor) {
        case SensorManager.SENSOR_ACCELEROMETER:

            currentOrientationValues[0] = values[0] * 0.1f + currentOrientationValues[0] * (1.0f - 0.1f);
            currentOrientationValues[1] = values[1] * 0.1f + currentOrientationValues[1] * (1.0f - 0.1f);
            currentOrientationValues[2] = values[2] * 0.1f + currentOrientationValues[2] * (1.0f - 0.1f);

            currentAccelerationValues[0] = values[0] - currentOrientationValues[0];
            currentAccelerationValues[1] = values[1] - currentOrientationValues[1];
            currentAccelerationValues[2] = values[2] - currentOrientationValues[2];

            float targetValue =
                Math.abs(currentAccelerationValues[0]) +
                Math.abs(currentAccelerationValues[1]) +
                Math.abs(currentAccelerationValues[2]);

            if(targetValue > 12.0f)
            {
                if(!waitFlag)
                {
                    valueArray.clear();
                    valueArray.add(targetValue);
                    waitFlag = true;
                    processHandler.postDelayed(processRunnable, 300);
                }
                else
                {
                    valueArray.add(targetValue);
                }
            }

            default:
        }
    }

    public void executeShake()
    {
        Collections.sort(valueArray);
        float result = valueArray.get(valueArray.size() -1);
        if(result > 20.0f) // Called if shaken strongly
        {
            taskSwitcher.onShake();
        }
        else // Called if shaken gently
        {
            taskSwitcher.onShake();
        }
    }

    public void onAccuracyChanged(int sensor, int accuracy) {

    }

}

TaskSwitcher.java

package biz.stachibana.ShakeAgent.worker;

import java.util.ArrayList;
import java.util.List;

import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;

public class TaskSwitcher {
    final String ACTIVITY_OF_THIS = "biz.stachibana.ShakeAgent.ShakeAgent";
    final String ANDROID_LAUNCHER = "com.android.launcher.Launcher";

    Context context;
    public TaskSwitcher(Context context) {
        this.context = context;
    }

    protected boolean isWaitingNext;
    Handler processHandler = new Handler();
    Runnable processRunnable = new Runnable(){
        public void run() {
        isWaitingNext = false;
    }};

    protected int currentIndex;
    ActivityManager am;
    List<ActivityManager.RecentTaskInfo> runningTasks = new ArrayList<ActivityManager.RecentTaskInfo>();
    public void onShake()
    {
        if(!isWaitingNext)
        {
            currentIndex = 1;
            am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
            List<ActivityManager.RecentTaskInfo> recentTaskList = am.getRecentTasks(30, ActivityManager.RECENT_WITH_EXCLUDED);
            runningTasks.clear();

            for(int i = 0; i < recentTaskList.size(); i++)
            {
                ActivityManager.RecentTaskInfo obj = recentTaskList.get(i);
                String objName = obj.baseIntent.getComponent().getClassName().toString();
                if(obj != null && !objName.equals(ACTIVITY_OF_THIS) && !objName.equals(ANDROID_LAUNCHER))
                {
                    runningTasks.add(obj);
                }
            }
        }
        if(runningTasks.size() < 2)
        {
            return ;
        }
        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_LAUNCHER);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        intent.setComponent(runningTasks.get(currentIndex).baseIntent.getComponent());

        context.startActivity(intent);
        currentIndex = currentIndex + 1 < runningTasks.size() ? currentIndex+1 : 0;
        isWaitingNext = true;
        processHandler.removeCallbacks(processRunnable);
        processHandler.postDelayed(processRunnable, 3000);
    }
}

TriggerOnBoot.java

package biz.stachibana.ShakeAgent;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class TriggerOnBoot extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if( "android.intent.action.BOOT_COMPLETED".equals(intent.getAction()))
        {
            Intent newIntent = new Intent(Intent.ACTION_VIEW);
            newIntent = new Intent(context, ShakeDetector.class);
            context.startService(newIntent);
        }
    }

}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="biz.stachibana.ShakeAgent"
      android:versionCode="1"
      android:versionName="1.0.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".ShakeAgent"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name=".ShakeDetector" />
        <receiver android:name=".TriggerOnBoot"
             android:enabled="true"
             android:exported="false"
             android:label="TriggerOnBoot">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
    </application>
    <uses-permission android:name="android.permission.GET_TASKS" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
</manifest>
EasyFreeAds Blog News Facebook Twitter Myspace Friendfeed Technorati del.icio.us Digg Google Yahoo Buzz StumbleUpon

1 Response to "【Android】端末を振ってタスクを切り替えるアプリケーションを作ってみました"

1 | gucci 財布 店舗

7月20日 2013 at 11:20 AM

Avatar

Flex、AIR、Java、Androidなど » 【Android】端末を振ってタスクを切り替えるアプリケーションを作ってみました

Categories

 

2017年4月
« 4月    
 12
3456789
10111213141516
17181920212223
24252627282930

About

Author: tachibana

  • ちょっとしたことはTwitterに書いています。こっちはアプリの公開等の時に更新されます。
  • 最近はもっぱらJavaとObjective Cです。AS3は飽きました。
  • スクリプト言語ではPerlが好きでしたが最近はGAE/Jで何でもやってます。
  • Linuxは自宅サーバー建てるのがやっとのレベルです。前の会社で何日も徹夜してやったのはいい思い出です。
  • アプリへのご要望などご意見等ありましたらお気軽にご連絡下さい。

Alternative content here