User Tools

Site Tools


android_learning:headfirst_android_development_notes:chapter_13

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
android_learning:headfirst_android_development_notes:chapter_13 [2016/03/14 03:19] mithatandroid_learning:headfirst_android_development_notes:chapter_13 [2016/05/07 06:04] (current) – [p. 553] mithat
Line 1: Line 1:
 ====== Chapter 13 ====== ====== Chapter 13 ======
  
-<WRAP center round info 60%> +<WRAP center round important 60%> 
-Under development.+If you read nothing else here, you should check out the section on [[#permissions]].
 </WRAP> </WRAP>
 +
 +===== p. 544 =====
 +
 +The //File > New > Service// menu has changed in Android Studio 1.5.1. Now there are explicit entries for the two Intent classes.
 +
 +===== p. 547: Code block =====
 +<file java DelayedMessageService.java>
 +package com.hfad.joke;
 +
 +import android.app.IntentService;
 +import android.content.Intent;
 +import android.util.Log;
 +
 +public class DelayedMessageService extends IntentService {
 +    public static final String EXTRA_MESSAGE = "message";
 +
 +    public DelayedMessageService() {
 +        super("DelayedMessageService");
 +    }
 +
 +    @Override
 +    protected void onHandleIntent(Intent intent) {
 +        synchronized (this) {
 +            try {
 +                wait(10000);
 +            } catch (InterruptedException e) {
 +                e.printStackTrace();
 +            }
 +        }
 +        String text = intent.getStringExtra(EXTRA_MESSAGE);
 +        showText(text);
 +    }
 +
 +    private void showText(final String text) {
 +        Log.v("DelayedMessageService", "The message is: " + text);
 +    }
 +}
 +</file>
 +
 +===== p. 550: Code block =====
 +<code java>
 +    public void onClick(View view) {
 +        Intent intent = new Intent(this, DelayedMessageService.class);
 +        intent.putExtra(DelayedMessageService.EXTRA_MESSAGE,
 +                getResources().getString(R.string.button_response));
 +        startService(intent);
 +    }
 +</code>
 +
 +===== p. 553 =====
 +
 +In other words, while most IntentService methods run off the main thread, but ''onStartCommand()'' runs on the main thread.
 +
 +===== p. 554 =====
 +
 +<file java DelayedMessageService.java>
 +package com.hfad.joke;
 +
 +import android.app.IntentService;
 +import android.content.Intent;
 +import android.os.Handler;
 +import android.widget.Toast;
 +
 +public class DelayedMessageService extends IntentService {
 +    public static final String EXTRA_MESSAGE = "message";
 +    private Handler handler;
 +
 +    public DelayedMessageService() {
 +        super("DelayedMessageService");
 +    }
 +
 +    @Override
 +    public int onStartCommand(Intent intent, int flags, int startId) {
 +        // This method runs on the main thread, so the Handler that's
 +        // instantiated here will also be running on the main thread.    
 +        handler = new Handler();
 +        return super.onStartCommand(intent, flags, startId);
 +    }
 +
 +    @Override
 +    protected void onHandleIntent(Intent intent) {
 +        synchronized (this) {
 +            try {
 +                wait(10000);
 +            } catch (InterruptedException e) {
 +                e.printStackTrace();
 +            }
 +        }
 +        String text = intent.getStringExtra(EXTRA_MESSAGE);
 +        showText(text);
 +    }
 +
 +    private void showText(final String text) {
 +        handler.post(new Runnable() {
 +            @Override
 +            public void run() {
 +                Toast.makeText(getApplicationContext(), text,
 +                    Toast.LENGTH_LONG).show();
 +            }
 +        });
 +    }
 +}
 +</file>
 +
 +
 +===== pp. 562-563: Code block =====
 +
 +<file java DelayedMessageService.java>
 +package com.hfad.joke;
 +
 +import android.app.IntentService;
 +import android.content.Intent;
 +import android.app.Notification;
 +import android.app.NotificationManager;
 +import android.app.PendingIntent;
 +import android.app.TaskStackBuilder;
 +import android.content.Context;
 +
 +public class DelayedMessageService extends IntentService {
 +    public static final String EXTRA_MESSAGE = "message";
 +    public static final int NOTIFICATION_ID = 5453;
 +
 +    public DelayedMessageService() {
 +        super("DelayedMessageService");
 +    }
 +
 +    @Override
 +    protected void onHandleIntent(Intent intent) {
 +        synchronized (this) {
 +            try {
 +                wait(10000);
 +            } catch (InterruptedException e) {
 +                e.printStackTrace();
 +            }
 +        }
 +        String text = intent.getStringExtra(EXTRA_MESSAGE);
 +        showText(text);
 +    }
 +
 +    private void showText(final String text) {
 +        Intent intent = new Intent(this, MainActivity.class);
 +        // No, really ... this is a common enough pattern, why doesn'
 +        // Android do the backstack/pendingIntent construction at a
 +        // higher level?
 +        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
 +        stackBuilder.addParentStack(MainActivity.class);
 +        stackBuilder.addNextIntent(intent);
 +        PendingIntent pendingIntent =
 +            stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
 +        Notification notification = new Notification.Builder(this)
 +            .setSmallIcon(R.mipmap.ic_launcher)
 +            .setContentTitle(getString(R.string.app_name))
 +            .setAutoCancel(true)
 +            .setPriority(Notification.PRIORITY_MAX)
 +            .setDefaults(Notification.DEFAULT_VIBRATE)
 +            .setContentIntent(pendingIntent)
 +            .setContentText(text)
 +            .build();
 +        NotificationManager notificationManager =
 +            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
 +        notificationManager.notify(NOTIFICATION_ID, notification);
 +    }
 +}
 +</file>
 +
 +===== pp. 580-581: Code block =====
 +
 +<file java OdometerService.java>
 +package com.hfad.odometer;
 +
 +import android.app.Service;
 +import android.content.Context;
 +import android.content.Intent;
 +import android.location.Location;
 +import android.location.LocationListener;
 +import android.location.LocationManager;
 +import android.os.Binder;
 +import android.os.Bundle;
 +import android.os.IBinder;
 +
 +public class OdometerService extends Service {
 +
 +    private final IBinder binder = new OdometerBinder();
 +    private static double distanceInMeters;
 +    private static Location lastLocation = null;
 +    private LocationListener listener;
 +    private LocationManager locManager;
 +
 +    public class OdometerBinder extends Binder {
 +        OdometerService getOdometer() {
 +            return OdometerService.this;
 +        }
 +    }
 +
 +    @Override
 +    public IBinder onBind(Intent intent) {
 +        return binder;
 +    }
 +
 +    @Override
 +    public void onCreate() {
 +        listener = new LocationListener() {
 +            @Override
 +            public void onLocationChanged(Location location) {
 +                if (lastLocation == null) {
 +                    lastLocation = location;
 +                }
 +                distanceInMeters += location.distanceTo(lastLocation);
 +                lastLocation = location;
 +            }
 +
 +            @Override
 +            public void onProviderDisabled(String arg0) {}
 +
 +            @Override
 +            public void onProviderEnabled(String arg0) {}
 +
 +            @Override
 +            public void onStatusChanged(String arg0, int arg1, Bundle bundle) {}
 +        };
 +        locManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
 +        locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, listener);
 +    }
 +
 +    @Override
 +    public void onDestroy() {
 +        if (locManager != null && listener != null) {
 +            locManager.removeUpdates(listener);
 +            locManager = null;
 +            listener = null;
 +        }
 +    }
 +
 +    public double getMiles() {
 +        return this.distanceInMeters / 1609.344;
 +    }
 +}
 +</file>
 +
 +===== Permissions? =====
 +
 +You will probably continue to see editor rage even after you add the location services permissions to //AndroidManifest.xml//. The autofix will suggest that you "Add Permission Check." This is because permission handling has [[https://developer.android.com/training/permissions/requesting.html|changed in Android 6.0 (API level 23)]]. Instead of the permissions being asked for at install time, they are now asked at run time.
 +
 +> If the device is running Android 5.1 or lower, **or** your app's target SDK is 22 or lower: If you list a dangerous permission in your manifest, the user has to grant the permission when they install the app; if they do not grant the permission, the system does not install the app at all.
 +>
 +> If the device is running Android 6.0 or higher, **and** your app's target SDK is 23 or higher: The app has to list the permissions in the manifest, and it must request each dangerous permission it needs while the app is running. The user can grant or deny each permission, and the app can continue to run with limited capabilities even if the user denies a permission request. 
 +
 +For the purposes of this app, we can work around this change by targeting SDK 22 instead of SDK 23. But if you plan on going further with Android development, you should carefully read the developer documentation regarding [[https://developer.android.com/training/permissions/requesting.html|requesting permissions]].
 +
 +To downgrade the target SDK to 22, with the left panel set to "Android" mode, open //Grade Scripts > Build.grade ()// and change
 +<code>
 +    defaultConfig {
 +        applicationId "com.hfad.odometer"
 +        minSdkVersion 16
 +        targetSdkVersion 23
 +        versionCode 1
 +        versionName "1.0"
 +    }
 +</code> to
 +<code>
 +    defaultConfig {
 +        applicationId "com.hfad.odometer"
 +        minSdkVersion 16
 +        targetSdkVersion 22
 +        versionCode 1
 +        versionName "1.0"
 +    }</code>
 +
 +The IDE will tell you that the project needs to be synced, which you should do. And for good measure, from the menu bar, do a //Build > Clean Project//.
 +
 +===== Code block =====
 +<file java MainActivity.java>
 +package com.hfad.odometer;
 +
 +import android.app.Activity;
 +import android.content.ComponentName;
 +import android.content.Context;
 +import android.content.Intent;
 +import android.content.ServiceConnection;
 +import android.os.Bundle;
 +import android.os.Handler;
 +import android.os.IBinder;
 +import android.widget.TextView;
 +
 +public class MainActivity extends Activity {
 +
 +    private OdometerService odometer;
 +    private boolean bound = false;
 +
 +    private ServiceConnection connection = new ServiceConnection() {
 +        @Override
 +        public void onServiceConnected(ComponentName componentName, IBinder binder) {
 +            OdometerService.OdometerBinder odometerBinder =
 +                    (OdometerService.OdometerBinder) binder;
 +            odometer = odometerBinder.getOdometer();
 +            bound = true;
 +        }
 +        @Override
 +        public void onServiceDisconnected(ComponentName componentName) {
 +            bound = false;
 +        }
 +    };
 +
 +    @Override
 +    protected void onCreate(Bundle savedInstanceState) {
 +        super.onCreate(savedInstanceState);
 +        setContentView(R.layout.activity_main);
 +        watchMileage();
 +    }
 +
 +    @Override
 +    protected void onStart() {
 +        super.onStart();
 +        Intent intent = new Intent(this, OdometerService.class);
 +        bindService(intent, connection, Context.BIND_AUTO_CREATE);
 +    }
 +
 +    @Override
 +    protected void onStop() {
 +        super.onStop();
 +        if (bound) {
 +            unbindService(connection);
 +            bound = false;
 +        }
 +    }
 +
 +    private void watchMileage() {
 +        final TextView distanceView = (TextView)findViewById(R.id.distance);
 +        final Handler handler = new Handler();
 +        handler.post(new Runnable() {
 +            @Override
 +            public void run() {
 +                double distance = 0.0;
 +                if (odometer != null) {
 +                    distance = odometer.getMiles();
 +                }
 +                String distanceStr = String.format("%1$,.2f miles", distance);
 +                distanceView.setText(distanceStr);
 +                handler.postDelayed(this, 1000);
 +            }
 +        });
 +    }
 +}
 +</file>
 +
android_learning/headfirst_android_development_notes/chapter_13.1457925588.txt.gz · Last modified: 2016/03/14 03:19 by mithat

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki