3

I know this is very lame but I'm totally new to android development. I'm writing a sensor based app which will change wallpaper each time the phone is shaked. Once the app is minimized it runs in background.

It works wonderfully when I run it for the first time.But When I minimize it and re-open it looks like 2 instances of the app are running. So it goes on. Every time I minimize and open the app, looks like one more instance is started in parallel.

Problem it causes:
1: Multiple instances of same app listening to "shake"
2: Multiple instances of same app trying to change wallpaper
3: Wallpaper change made by the the last instance of the app is noticeable

I tried setting below:
android:clearTaskOnLaunch="true"
android:launchMode="singleInstance"

Nothing works. Kindly help.

Below is my activity class & Manifext file

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="abhijit.android.sensor"
        android:versionCode="1"
        android:versionName="1.0" >

        <uses-sdk
            android:minSdkVersion="14"
            android:targetSdkVersion="19" />
         <uses-permission android:name="android.permission.VIBRATE" />
         <uses-permission android:name="android.permission.SET_WALLPAPER" />

        <application
            android:name="abhijit.android.sensor.GlobalVarible"
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >
            <activity
                android:name="abhijit.android.sensor.SensorTestActivity" 
                android:label="@string/app_name" 
                android:clearTaskOnLaunch="true"
                android:launchMode="singleInstance" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>

    </manifest>

SensorTestActivity.java

    package abhijit.android.sensor;

    import java.io.IOException;
    import java.util.Date;
    import java.util.Random;

    import android.app.Activity;
    import android.app.AlertDialog;
    import android.app.WallpaperManager;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.graphics.Color;
    import android.hardware.Sensor;
    import android.hardware.SensorEvent;
    import android.hardware.SensorEventListener;
    import android.hardware.SensorManager;
    import android.os.Bundle;
    import android.os.Vibrator;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    import android.widget.Toast;

    public class SensorTestActivity extends Activity implements SensorEventListener {
      private SensorManager sensorManager;
      private boolean color = false;
      private View view;
      TextView text;
      private long lastUpdate;
      Random rnd = new Random(); 
      int colors; 
      private Vibrator vibrate;
      GlobalVarible globalVariable;
      WallpaperManager myWallpaperManager;
      Button b1,b2,b3;
      private static String AppVersion ="WALL-e v0.7 (Beta)";


    /** Called when the activity is first created. */

      @Override
      public void onCreate(Bundle savedInstanceState) {
    //    requestWindowFeature(Window.FEATURE_NO_TITLE);
    //    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sensor_test);
        view = findViewById(R.id.textView);
        text = (TextView) findViewById(R.id.textView);
        view.setBackgroundColor(Color.GREEN);
        b1 = (Button) findViewById(R.id.Enable_button);
        b2 = (Button) findViewById(R.id.Dis_button);
        b3 = (Button) findViewById(R.id.Exit_button);


        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        vibrate = (Vibrator) getSystemService(VIBRATOR_SERVICE);
        myWallpaperManager = WallpaperManager.getInstance(getApplicationContext());
        lastUpdate = new Date().getTime();

        // Calling Application class (see application tag in AndroidManifest.xml)
        globalVariable = (GlobalVarible) getApplicationContext();

        //Set name and email in global/application context
        globalVariable.setCounter(0);
        globalVariable.setWall(1);
        System.out.println("Counter set to :" + globalVariable.getCounter());    

      }

      @Override
      public void onSensorChanged(SensorEvent event) 
      {
        if  ((event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)  & globalVariable.appEnabled) {getAccelerometer(event);}
      }
      private void getAccelerometer(SensorEvent event) {


        float[] values = event.values;
        // Movement
        float x = values[0];
        float y = values[1];
        float z = values[2];


        float accelationSquareRoot = (x * x + y * y + z * z) / (SensorManager.GRAVITY_EARTH * SensorManager.GRAVITY_EARTH);
    //    long actualTime = event.timestamp;

        long actualTime = (new Date()).getTime()  + (event.timestamp - System.nanoTime()) / 1000000L;

        if (accelationSquareRoot >= 3) //
        {
            long timediff = actualTime - lastUpdate;

          if (timediff < 1000) {
              return;
          }

    //      Toast.makeText(this, "Detected device movement & working !!! ", Toast.LENGTH_SHORT).show();
          globalVariable.setCounter(globalVariable.getCounter()+1);

          vibrate.vibrate(300);

          if (color) 
          {
              colors= Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));
              view.setBackgroundColor(colors);
          } else {
             view.setBackgroundColor(colors);
          }
          color = !color;

          System.out.println("Counter # " + globalVariable.getCounter());

          if(globalVariable.getCounter()==1)
          {

              globalVariable.setCounter(0);               

              try 
              {
                  switch (globalVariable.getWall()) 
                  {
                            case 1:myWallpaperManager.setResource(R.drawable.wall1);break;              
                            case 2:myWallpaperManager.setResource(R.drawable.wall2);break;
                            case 3:myWallpaperManager.setResource(R.drawable.wall3);break;
                            case 4:myWallpaperManager.setResource(R.drawable.wall4);break;
                            case 5:myWallpaperManager.setResource(R.drawable.wall5);break;
                            case 6:myWallpaperManager.setResource(R.drawable.wall6);break;
                            case 7:myWallpaperManager.setResource(R.drawable.wall7);break;
                            case 8:myWallpaperManager.setResource(R.drawable.wall8);break;
                            case 9:myWallpaperManager.setResource(R.drawable.wall9);break;
                            case 10:myWallpaperManager.setResource(R.drawable.wall10);break;
                            case 11:myWallpaperManager.setResource(R.drawable.wall11);break;
                            case 12:myWallpaperManager.setResource(R.drawable.wall12);break;
                            case 13:myWallpaperManager.setResource(R.drawable.wall13);break;
                            case 14:myWallpaperManager.setResource(R.drawable.wall14);break;
                            case 15:myWallpaperManager.setResource(R.drawable.wall15);break;
                            case 16:myWallpaperManager.setResource(R.drawable.wall16);break;
                            case 17:myWallpaperManager.setResource(R.drawable.wall17);break;
                            case 18:myWallpaperManager.setResource(R.drawable.wall18);break;
                            case 19:myWallpaperManager.setResource(R.drawable.wall19);break;
                            case 20:myWallpaperManager.setResource(R.drawable.wall20);break;
                            case 21:myWallpaperManager.setResource(R.drawable.wall21);break;
                            case 22:myWallpaperManager.setResource(R.drawable.wall22);break;
                            case 23:myWallpaperManager.setResource(R.drawable.wall23);break;
                            case 24:myWallpaperManager.setResource(R.drawable.wall24);break;
                            case 25:myWallpaperManager.setResource(R.drawable.wall25);break;
                            case 26:myWallpaperManager.setResource(R.drawable.wall26);break;
                            case 27:myWallpaperManager.setResource(R.drawable.wall27);break;
                            default:break;
                  }           

                   Toast.makeText(this, "Successfully set as wallpaper to :"+globalVariable.getWall(), Toast.LENGTH_SHORT).show();
                   System.out.println("Counter : Wallerpaper Set to #"+globalVariable.getWall());

                   globalVariable.setWall(globalVariable.getWall()+1); 
                   if(globalVariable.getWall()>27) globalVariable.setWall(1);

                   lastUpdate = actualTime;

                  } catch (IOException e) 
                  {Toast.makeText(this, "Error set as wallpaper :", Toast.LENGTH_SHORT).show();}

          }      
        }
      }

      @Override
      public void onAccuracyChanged(Sensor sensor, int accuracy) {

      }

      @Override
      protected void onResume() {
        super.onResume();
        // register this class as a listener for the orientation and
        // accelerometer sensors
        sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),SensorManager.SENSOR_DELAY_NORMAL);
        Toast.makeText(this, "Sensor detection resumed !!!", Toast.LENGTH_SHORT).show();
      }

      @Override
      protected void onPause() {
        // unregister listener
        super.onPause();
    //    sensorManager.unregisterListener(this);
        Toast.makeText(this, "Sensor detection running in background !!!", Toast.LENGTH_SHORT).show();
    //    System.exit(0);
      }
      public void b1_Onclick(View v)
      {
          System.out.println("new Enabled!!!");
          globalVariable.appEnabled=true;
          sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),SensorManager.SENSOR_DELAY_NORMAL);
          text.setText("Application is [ ENABLED ]");

      }

      public void b2_Onclick(View v)
      {
          System.out.println("new Disbled!!!");
          globalVariable.appEnabled=false;
          sensorManager.unregisterListener(this);
          text.setText("Application is [ DISABLED ]");
      }

      public void b3_Onclick(View v)
      {
          System.out.println("new exit!!!");
          sensorManager.unregisterListener(this);
          this.onPause();
          this.finish();
      }

      public void b4_Onclick(View view)
      {

              AlertDialog.Builder dlgAlert  = new AlertDialog.Builder(this);

              dlgAlert.setMessage(AppVersion + " \n \nCopyright \u00a9 2014, \nAbhijit Mishra");
              dlgAlert.setTitle(AppVersion);
              dlgAlert.setPositiveButton("OK", null);
              dlgAlert.setCancelable(true);
              dlgAlert.create().show();

              dlgAlert.setPositiveButton("Ok", new DialogInterface.OnClickListener(){public void onClick(DialogInterface dialog, int which){}});      
      }


    } 

GlobalVariable.Java

    package abhijit.android.sensor;

    import android.app.Application;

    public class GlobalVarible extends Application{


       public int counter,wall;
       boolean appEnabled=true;


       public int getWall() {
        return wall;
    }

    public void setWall(int wall) {
        this.wall = wall;
    }

    public int getCounter() {
        return counter;
    }

       public void setCounter(int counter) {
           this.counter = counter;
    }




    }

Please let me know what should I do next so that Only 1 thread of the the application runs even if I minimize and bring it back up.

Regards, Abhijit

Abhijit
  • 33
  • 6
  • Don't do yourself down, that sounds like quite a nice idea – Richard Tingle Jul 04 '14 at 16:38
  • Let my first app be ready. I have named it WALL-e (of course after the movie :D). waiting for some help. – Abhijit Jul 04 '14 at 16:44
  • I haven't done anything with apps running in the background so my help is going to be limited. But to take a wild stab in the dark: I note that on each `onResume()` a new listener is registered but in `onPause()` the listener isn't deregisted (despite the comment claiming that it is) – Richard Tingle Jul 04 '14 at 16:47
  • I commented out the deregister part in onPause so that I can see the wallpaper changing after I minize this app. If I deregister in "onPause" it wont be able to detect motion when it's minimized. Is there a way to check if a listener is already registered? so that in "onResume" I dont have to add it blindly? – Abhijit Jul 04 '14 at 16:53
  • I'm slightly suprised its in onResume at all. Would it be fair to say you have to close and minimise your app before it starts working at all? I would have registered the listener within `onCreate()` – Richard Tingle Jul 04 '14 at 16:55
  • @RichardTingle `onResume()` (despite its name) executes the first time too :) – matiash Jul 04 '14 at 17:01
  • @matiash You make a very fair point, still the wrong place for it but the minimise/resume cycle won't be nessissary before it starts working – Richard Tingle Jul 04 '14 at 17:02
  • matiash & RichardTingle .. thank you guys for answering my queries so soon. truely appreciate it. – Abhijit Jul 04 '14 at 17:25

1 Answers1

3

The problem is here:

  @Override
  protected void onResume() {
    super.onResume();
    // register this class as a listener for the orientation and
    // accelerometer sensors
    sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),SensorManager.SENSOR_DELAY_NORMAL);
  }

  @Override
  protected void onPause() {
    // unregister listener
    // sensorManager.unregisterListener(this);
  }

You're not unregistering the listener, so it is registered multiple times.

I understand that what you want to do is to keep listening even if the activity is paused, and you could use a boolean flag to only register once. But it's probably not a good idea. Activites that are not visible can be finished by the system at any time.

For these kinds of use cases a Service would be far more appropriate (as a bonus, you could set it up to start on BOOT_COMPLETED, so that you don't need to re-run the app to set up this listener when you restart the device).

So, in short, I would recommend:

  • Create a Service. In onCreate(), register the service as a listener to SensorManager (very much as you do here).
  • When your activity runs, send an Intent to start the service (see docs).
  • Optionally, specify a listener for ACTION_BOOT_COMPLETED so that the service is restarted when the device restarts. See this answer.
Community
  • 1
  • 1
matiash
  • 54,791
  • 16
  • 125
  • 154
  • Fantastic. made a few changes for the time being n it worked... Added a boolean flag to check if the sensor is already registered. It solved my current problem. Step two is modifying it as "service". I'll do it pretty soon and keep you guys posted. Thanks a lot. – Abhijit Jul 04 '14 at 17:22