Create temporary in-app lock

7

In the app I am developing there is a usage policy, and if this policy is cheated I want to suspend the use of the app for 30 mins, how can I do that?

    
asked by anonymous 02.05.2016 / 19:14

3 answers

10

You must implement something that lets the application know if it is locked or not.

This information can, as the regmoraes said, be stored in Shared Preferences .

The question now is how to know that it took 30 minutes to unlock it.

A workaround would be to save, not a flag (true / false), but the timestamp date in milliseconds where the application was blocked. > To check if 30 minutes have elapsed, just subtract between the current date and the recorded date.

Method to block:

private void block(){
    SharedPreferences sharedPref = getPreferences("Pref", Context.MODE_PRIVATE);
    SharedPreferences.Editor editor = sharedPref.edit();
    editor.putLong("BlockTime", new Date().getTime());
    editor.commit();
}

Method to check if it is locked:

private boolean isBlocked(){
    SharedPreferences sharedPref = getPreferences("Pref", Context.MODE_PRIVATE);
    long blockTime = sharedPref.getLong("BlockTime", 0);
    return new Date().getTime() - blockTime < 1800000; //30*60*1000
}

EDIT:

You can prevent the system from being duped by the user if it changes the system date / time, as follows:

In addition to saving timestamp also save a flag that indicates whether it is locked or not:

private void block(){
    SharedPreferences sharedPref = getPreferences("Pref", Context.MODE_PRIVATE);
    SharedPreferences.Editor editor = sharedPref.edit();
    editor.putLong("BlockTime", new Date().getTime());
    editor.putBoolean("IsBlocked", true);
    editor.commit();
}

Declare a BroadcastReceiver that responds to the android.intent.action.TIME_SET action. It will be called when there is a system date / time change:

TimeChangeReceiver.java

public class TimeChangeReceiver extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {

        //Houve uma alteração na data/hora do sistema
        SharedPreferences sharedPref = getPreferences("Pref", Context.MODE_PRIVATE);
        boolean isBlocked = sharedPref.getBoolean("IsBlocked", false);
        if(isBlocked){
            //Grava novo timestamp
            SharedPreferences.Editor editor = sharedPref.edit();
            editor.putLong("BlockTime", new Date().getTime());
            editor.commit();
        }
    }    
}

Register receiver in AndroidManifest.xml :

<receiver android:name=".TimeChangeReceiver">
    <intent-filter>
        <action android:name="android.intent.action.TIME_SET"/>
    </intent-filter>
</receiver>

Change the isBlocked() method to handle flag :

private boolean isBlocked(){
    SharedPreferences sharedPref = getPreferences("Pref", Context.MODE_PRIVATE);
    long blockTime = sharedPref.getLong("BlockTime", 0);
    boolean isBlocked = new Date().getTime() - blockTime < 1800000; //30*60*1000
    if(!isBlocked){
        SharedPreferences.Editor editor = sharedPref.edit();
        editor.putBoolean("IsBlocked", false);
        editor.commit();
    }
    return isBlocked
}

Call method isBlocked() on onResesume()

My only question is whether or not small timing changes due to automatic synchronizations (if they are on) will or will not launch Receiver     

03.05.2016 / 12:38
5

The solution that @PauloGustavo gave in the comments is a good way out, but I would make a small change:

You'd better do the validation on the onResume() method of the activity, because this method is always called when opening an activity, even though it was just paused . Since onCreate() is only called once (when the Activity is created), therefore, a "rogue" user could "cheat" the verification as follows:

  • The user violates the usage policy
  • The app hangs
  • The user exits the app by placing it in the background (by pressing the home key, for example)
  • The user opens the list of recent apps and chooses your app
  • User re-fits your app normally!
  • This happens because in this round trip, your activity was not destroyed, it was only stopped, that means that when you open it again, the onCreate() method will not be called

    Another tip I give is:

    So when the user hits the usage policy, you do this in your activity:

    SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
    SharedPreferences.Editor editor = sharedPref.edit();
    editor.putBoolean(getString(R.string.bloqueado), true);
    editor.commit(); 
    

    If you want to "unlock" the app, just write the same code above, changing true to false .

    To treat the condition of "app locked" or not, place the following code in the onResume() method of your Activity:

    SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
    boolean bloqueado = sharedPref.getBoolean(getString(R.string.bloqueado), /* valor default*/ );
    
    if(bloqueado){
    
        //Notifica o usuário ou fecha a Activity
    
    }else{
    
       //Continue a execução da Activity normalmente
    }
    
        
    02.05.2016 / 20:53
    1

    As the blocking has already been answered, the question now is how to unblock. I think making a service background would be the good solution.

    As @ramaral suggested and @Fernando commented, the blocking through time would be somewhat mocking and require that the APP has an internet connection is also very risky seeing that he can never have more after the blockage and should this happen , you will never have an unlock.

    I believe that by creating a service we can get the result of the lock without the user cheating.

    Create a service that looks something like this:

        public class MyService extends Service {
    
        private Timer timer;
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            timer = new Timer();
            timer.schedule(timerTask, 2000, 5000); //2000 representa o tempo da primeira chamada e 5000 das demais (milissegundos)
            return super.onStartCommand(intent, flags, startId);
        }
    
    private TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
    
                Handler handler = new Handler(Looper.getMainLooper());
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        //do something
    
                        // stopSelf()
                    }
                });
    
            }
        };
    
    
        }
    

    The problem with this approach is that if the machine is turned off, the service will be lost. If this happens, you will probably have to implement it to start the service again at boot time.

        
    03.05.2016 / 15:17