Debug: AndroidStudio error (Cannot locate factory)

Debug: AndroidStudio error

This error was encountered by  Eddie on 8Jan19

Gradle 'MyApp' refresh failed.

Error:Cannot locate factory for objects of type DefaultGradleConnector, as ConnectorServiceRegistry has been closed.



Gradle 'MyApp' refresh failed. Error:Cannot locate factory for objects of type

You can restart android studio like this:
Click File ----> Invalidate Caches /Restart

Debug: SQL Error 1 column(s)








INSERT INTO `users`(`id`, `userid`, `password`) VALUES (10, 'aa', 'abc'), (11, 'bb', 'abc'), (12, 'cc', 'abc')

Remove 2 brackets after values

The wrong one is

INSERT INTO `users`(`id`, `userid`, `password`) VALUES ( (10, 'aa', 'abc'), (11, 'bb', 'abc'), (12, 'cc', 'abc') )

To insert more values with Python use for loop

https://stackoverflow.com/questions/14011160/how-to-use-python-mysqldb-to-insert-many-rows-at-once

Signature Pad Example


Signature Pad Example

To create a Signature Pad in Android App





Step 1: Goto build.gradle[ Module:App]

Add the followings as shown in the picture below

repositories {
    mavenCentral()
}

compile 'com.github.gcacace:signature-pad:1.2.1'










Set Permissions at AndroidManifest.xml

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<uses-permission android:name="android.permission.INTERNET" />












Step 2: Add the following to Strings.xml


<resources>
    <string name="app_name">SignaturePad</string>
    <string name="agreement">I agree to the terms and conditions.</string>
    <string name="clear_pad">Clear Pad</string>
    <string name="save_signature">Save Signature</string>
</resources>

Step 3: create a Values XML file name as dimens.xml
Right clicked on App> New> XML > Values XML file



<?xml version="1.0" encoding="utf-8"?>
<!-- Default screen margins, per the Android Design guidelines. -->

<resources>
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>


</resources>

Step 4: create a layout file for activity_main.xml




For a quick start, you may copy and paste the XML codes onto activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:signature="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <RelativeLayout
        android:id="@+id/signature_pad_container"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginBottom="52dp">

        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_below="@+id/signature_pad"
            android:text="Signature"
            android:textSize="36sp" />

        <com.github.gcacace.signaturepad.views.SignaturePad
            android:id="@+id/signature_pad"
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_alignParentTop="true" />

    </RelativeLayout>


    <LinearLayout
        android:id="@+id/buttons_container"
        android:paddingTop="16dp"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_alignParentBottom="true">

        <Button
            android:id="@+id/clear_button"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:text="@string/clear_pad"
            android:enabled="false" />

        <Button
            android:id="@+id/save_button"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:text="@string/save_signature"
            android:enabled="false" />

    </LinearLayout>

    <TextView
        android:id="@+id/signature_pad_description"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/buttons_container"
        android:layout_centerHorizontal="true"
        android:text="@string/agreement"
        android:textColor="@android:color/darker_gray" />
</RelativeLayout>


For a quick start, you may copy and paste the java codes onto MainActivity.java
package signature.mdad.signaturepad;

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import com.github.gcacace.signaturepad.views.SignaturePad;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;



public class MainActivity extends Activity {

    private static final int REQUEST_EXTERNAL_STORAGE = 1;
    private static String[] PERMISSIONS_STORAGE = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
    private SignaturePad mSignaturePad;
    private Button mClearButton;
    private Button mSaveButton;


    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        verifyStoragePermissions(this);
        setContentView(R.layout.activity_main);

        mSignaturePad = (SignaturePad) findViewById(R.id.signature_pad);
        mSignaturePad.setOnSignedListener(new SignaturePad.OnSignedListener() {
            @Override
            public void onStartSigning() {
                Toast.makeText(MainActivity.this, "OnStartSigning", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onSigned() {
                mSaveButton.setEnabled(true);
                mClearButton.setEnabled(true);
            }

            @Override
            public void onClear() {
                mSaveButton.setEnabled(false);
                mClearButton.setEnabled(false);
            }

        });

        mClearButton = (Button) findViewById(R.id.clear_button);
        mSaveButton = (Button) findViewById(R.id.save_button);
        mClearButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mSignaturePad.clear();
            }

        });

        mSaveButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Bitmap signatureBitmap = mSignaturePad.getSignatureBitmap();
                if (addJpgSignatureToGallery(signatureBitmap)) {
                    Toast.makeText(MainActivity.this, "Signature saved into the Gallery", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(MainActivity.this, "Unable to store the signature", Toast.LENGTH_SHORT).show();
                }

                if (addSvgSignatureToGallery(mSignaturePad.getSignatureSvg())) {
                    Toast.makeText(MainActivity.this, "SVG Signature saved into the Gallery", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(MainActivity.this, "Unable to store the SVG signature", Toast.LENGTH_SHORT).show();
                }
            }
        });
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {

        switch (requestCode) {
            case REQUEST_EXTERNAL_STORAGE: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length <= 0
                        || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(MainActivity.this, "Cannot write images to external storage", Toast.LENGTH_SHORT).show();
                }
            }
        }
    }

    public File getAlbumStorageDir(String albumName) {
        // Get the directory for the user's public pictures directory.
        File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), albumName);
        if (!file.mkdirs()) {
            Log.e("SignaturePad", "Directory not created");
        }
        return file;
    }


    public void saveBitmapToJPG(Bitmap bitmap, File photo) throws IOException {
        Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(newBitmap);
        canvas.drawColor(Color.WHITE);
        canvas.drawBitmap(bitmap, 0, 0, null);
        OutputStream stream = new FileOutputStream(photo);
        newBitmap.compress(Bitmap.CompressFormat.JPEG, 80, stream);
        stream.close();
    }


    public boolean addJpgSignatureToGallery(Bitmap signature) {
        boolean result = false;
        try {
            File photo = new File(getAlbumStorageDir("SignaturePad"), String.format("Signature_%d.jpg", System.currentTimeMillis()));
            saveBitmapToJPG(signature, photo);
            scanMediaFile(photo);
            result = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    private void scanMediaFile(File photo) {
        Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        Uri contentUri = Uri.fromFile(photo);
        mediaScanIntent.setData(contentUri);
        MainActivity.this.sendBroadcast(mediaScanIntent);
    }



    public boolean addSvgSignatureToGallery(String signatureSvg) {
        boolean result = false;
        try {
            File svgFile = new File(getAlbumStorageDir("SignaturePad"), String.format("Signature_%d.svg", System.currentTimeMillis()));
            OutputStream stream = new FileOutputStream(svgFile);
            OutputStreamWriter writer = new OutputStreamWriter(stream);
            writer.write(signatureSvg);
            writer.close();
            stream.flush();
            stream.close();
            scanMediaFile(svgFile);
            result = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }



    /**
     * Checks if the app has permission to write to device storage
     * <p/>

     * If the app does not has permission then the user will be prompted to grant permissions
     *
     * @param activity the activity from which permissions are checked
     */
    public static void verifyStoragePermissions(Activity activity) {

        // Check if we have write permission
        int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
        if (permission != PackageManager.PERMISSION_GRANTED) {
            // We don't have permission so prompt the user
            ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
        }
    }
}

Step 5: Build and Run the App

CannonGame App

CannonGame App

Objective: To create a mobile game: CannonGame App




1.    Create a new Android Project called CannonGame with package mdad.game and the activity name as CannonController.
2.    Open the strings.xml file and add the following strings: (DO NOT modify the ones in italics)


<r<resources>
    <string name="app_name">CannonGame</string>
    <string name="hello_world">Hello world!</string>
    <string name="action_settings">Settings</string>
    <string name="results_format">Shots fired: %1$d\nTotal time: %2$.1f</string>
    <string name="reset_game">Reset Game</string>
    <string name="win">You win!</string>
    <string name="lose">You lose!</string>
    <string name="time_remaining_format">Time remaining: %.1f seconds</string>

</resources>

3. Add the highlighted codes in the layout activity_cannon_controller.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <mdad.game.CannonView android:background="@android:color/transparent"
        android:id="@+id/cannonView" android:layout_height="match_parent"
        android:layout_width="match_parent"
        xmlns:android="http://schemas.android.com/apk/res/android"/>

</android.support.constraint.ConstraintLayout>



4. Create new folder raw in res folder and drag the 3 sound (.wav) files into it. 

The sound files can be downloaded here




















5.    Create a new Java class CannonModel, in the same package, as shown: (this is the class to create lines and other shapes). Fill in the missing codes (non-italics portion)
papackage mdad.game;

import android.graphics.Point;

public class CannonModel {
    public Point start; // starting Point
    public Point end; // ending Point

    // default constructor initializes Points to (0, 0)
    public CannonModel() {
        start = new Point(0, 0); // start Point
        end = new Point(0, 0); // end Point
    } // end constructor
}// end class


6. Create a new class called CannonView (same package name). 

papackage mdad.game;


import java.util.HashMap;
import java.util.Map;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.media.AudioManager;
import android.media.SoundPool;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class CannonView extends SurfaceView implements SurfaceHolder.Callback {

    private CannonThread cannonThread; // controls the game loop
    private Activity activity; // to display Game Over dialog in GUI thread
    private boolean dialogIsDisplayed = false;

    // constants for game play
    public static final int TARGET_PIECES = 7; // sections in the target
    public static final int MISS_PENALTY = 2; // seconds deducted on a miss
    public static final int HIT_REWARD = 3; // seconds added on a hit

    // variables for the game loop and tracking statistics
    private boolean gameOver; // is the game over?
    private double timeLeft; // the amount of time left in seconds
    private int shotsFired; // the number of shots the user has fired
    private double totalElapsedTime; // the number of seconds elapsed

    // variables for the blocker and target
    private CannonModel blocker; // start and end points of the blocker
    private int blockerDistance; // blocker distance from left
    private int blockerBeginning; // blocker distance from top
    private int blockerEnd; // blocker bottom edge distance from top
    private int initialBlockerVelocity; // initial blocker speed multiplier
    private float blockerVelocity; // blocker speed multiplier during game

    private CannonModel target; // start and end points of the target
    private int targetDistance; // target distance from left
    private int targetBeginning; // target distance from top
    private double pieceLength; // length of a target piece
    private int targetEnd; // target bottom's distance from top
    private int initialTargetVelocity; // initial target speed multiplier
    private float targetVelocity; // target speed multiplier during game

    private int lineWidth; // width of the target and blocker
    private boolean[] hitStates; // is each target piece hit?
    private int targetPiecesHit; // number of target pieces hit (out of 7)

    // variables for the cannon and cannonball
    private Point cannonball; // cannonball image's upper-left corner
    private int cannonballVelocityX; // cannonball's x velocity
    private int cannonballVelocityY; // cannonball's y velocity
    private boolean cannonballOnScreen; // is the cannonball on the screen
    private int cannonballRadius; // cannonball radius
    private int cannonballSpeed; // cannonball speed
    private int cannonBaseRadius; // cannon base radius
    private int cannonLength; // cannon barrel length
    private Point barrelEnd; // the endpoint of the cannon's barrel
    private int screenWidth; // width of the screen
    private int screenHeight; // height of the screen

    // constants and variables for managing sounds
    private static final int TARGET_SOUND_ID = 0;
    private static final int CANNON_SOUND_ID = 1;
    private static final int BLOCKER_SOUND_ID = 2;
    private SoundPool soundPool; // plays sound effects
    private Map<Integer, Integer> soundMap; // maps IDs to SoundPool

    // Paint variables used when drawing each item on the screen
    private Paint textPaint; // Paint used to draw text
    private Paint cannonballPaint; // Paint used to draw the cannonball
    private Paint cannonPaint; // Paint used to draw the cannon
    private Paint blockerPaint; // Paint used to draw the blocker
    private Paint targetPaint; // Paint used to draw the target
    private Paint backgroundPaint; // Paint used to clear the drawing area

    // public constructor
    public CannonView(Context context, AttributeSet attrs)
    {
        super(context, attrs); // call super's constructor
        activity = (Activity) context;

        // register SurfaceHolder.Callback listener
        getHolder().addCallback(this);

        // initialize Lines and points representing game items
        blocker = new CannonModel(); // create the blocker as a Line
        target = new CannonModel(); // create the target as a Line
        cannonball = new Point(); // create the cannonball as a point

        // initialize hitStates as a boolean array
        hitStates = new boolean[TARGET_PIECES];

        // initialize SoundPool to play the app's three sound effects
        soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);

        // create Map of sounds and pre-load sounds
        soundMap = new HashMap<Integer, Integer>(); // create new HashMap
        soundMap.put(TARGET_SOUND_ID,
                soundPool.load(context, R.raw.target_hit, 1));
        soundMap.put(CANNON_SOUND_ID,
                soundPool.load(context, R.raw.cannon_fire, 1));
        soundMap.put(BLOCKER_SOUND_ID,
                soundPool.load(context, R.raw.blocker_hit, 1));

        // construct Paints for drawing text, cannonball, cannon,
        // blocker and target; these are configured in method onSizeChanged
        textPaint = new Paint(); // Paint for drawing text
        cannonPaint = new Paint(); // Paint for drawing the cannon
        cannonballPaint = new Paint(); // Paint for drawing a cannonball
        blockerPaint = new Paint(); // Paint for drawing the blocker
        targetPaint = new Paint(); // Paint for drawing the target
        backgroundPaint = new Paint(); // Paint for drawing the target
    } // end CannonView constructor

    // called by surfaceChanged when the size of the SurfaceView changes,
    // such as when it's first added to the View hierarchy
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh)
    {
        super.onSizeChanged(w, h, oldw, oldh);

        screenWidth = w; // store the width
        screenHeight = h; // store the height
        cannonBaseRadius = h / 18; // cannon base radius 1/18 screen height
        cannonLength = w / 8; // cannon length 1/8 screen width

        cannonballRadius = w / 36; // cannonball radius 1/36 screen width
        cannonballSpeed = w * 3 / 2; // cannonball speed multiplier

        lineWidth = w / 24; // target and blocker 1/24 screen width

        // configure instance variables related to the blocker
        blockerDistance = w * 5 / 8; // blocker 5/8 screen width from left
        blockerBeginning = h / 8; // distance from top 1/8 screen height
        blockerEnd = h * 3 / 8; // distance from top 3/8 screen height
        initialBlockerVelocity = h / 2; // initial blocker speed multiplier
        blocker.start = new Point(blockerDistance, blockerBeginning);
        blocker.end = new Point(blockerDistance, blockerEnd);

        // configure instance variables related to the target
        targetDistance = w * 7 / 8; // target 7/8 screen width from left
        targetBeginning = h / 8; // distance from top 1/8 screen height
        targetEnd = h * 7 / 8; // distance from top 7/8 screen height
        pieceLength = (targetEnd - targetBeginning) / TARGET_PIECES;
        initialTargetVelocity = -h / 4; // initial target speed multiplier
        target.start = new Point(targetDistance, targetBeginning);
        target.end = new Point(targetDistance, targetEnd);

        // endpoint of the cannon's barrel initially points horizontally
        barrelEnd = new Point(cannonLength, h / 2);

        // configure Paint objects for drawing game elements
        textPaint.setTextSize(w / 20); // text size 1/20 of screen width
        textPaint.setAntiAlias(true); // smoothes the text
        cannonPaint.setStrokeWidth(lineWidth * 1.5f); // set line thickness
        blockerPaint.setStrokeWidth(lineWidth); // set line thickness
        targetPaint.setStrokeWidth(lineWidth); // set line thickness
        backgroundPaint.setColor(Color.WHITE); // set background color

        newGame(); // set up and start a new game
    } // end method onSizeChanged

    // reset all the screen elements and start a new game
    public void newGame()
    {
        // set every element of hitStates to false--restores target pieces
        for (int i = 0; i < TARGET_PIECES; ++i)
            hitStates[i] = false;

        targetPiecesHit = 0; // no target pieces have been hit
        blockerVelocity = initialBlockerVelocity; // set initial velocity
        targetVelocity = initialTargetVelocity; // set initial velocity
        timeLeft = 10;  // start the countdown at 10 seconds
        cannonballOnScreen = false; // the cannonball is not on the screen
        shotsFired = 0; // set the initial number of shots fired
        totalElapsedTime = 0.0; // set the time elapsed to zero
        blocker.start.set(blockerDistance, blockerBeginning);
        blocker.end.set(blockerDistance, blockerEnd);
        target.start.set(targetDistance, targetBeginning);
        target.end.set(targetDistance, targetEnd);

        if (gameOver)
        {
            gameOver = false; // the game is not over
            cannonThread = new CannonThread(getHolder());
            cannonThread.start();
        } // end if
    } // end method newGame

    // called repeatedly by the CannonThread to update game elements
    private void updatePositions(double elapsedTimeMS)
    {
        double interval = elapsedTimeMS / 1000.0; // convert to seconds

        if (cannonballOnScreen) // if there is currently a shot fired
        {
            // update cannonball position
            cannonball.x += interval * cannonballVelocityX;
            cannonball.y += interval * cannonballVelocityY;

            // check for collision with blocker
            if (cannonball.x + cannonballRadius > blockerDistance &&
                    cannonball.x - cannonballRadius < blockerDistance &&
                    cannonball.y + cannonballRadius > blocker.start.y &&
                    cannonball.y - cannonballRadius < blocker.end.y)
            {
                cannonballVelocityX *= -1; // reverse cannonball's direction
                timeLeft -= MISS_PENALTY; // penalize the user

                // play blocker sound
                soundPool.play(soundMap.get(BLOCKER_SOUND_ID), 1, 1, 1, 0, 1f);
            } // end if

            // check for collisions with left and right walls
            else if (cannonball.x + cannonballRadius > screenWidth ||
                    cannonball.x - cannonballRadius < 0)
                cannonballOnScreen = false; // remove cannonball from screen

                // check for collisions with top and bottom walls
            else if (cannonball.y + cannonballRadius > screenHeight ||
                    cannonball.y - cannonballRadius < 0)
                cannonballOnScreen = false; // make the cannonball disappear

                // check for cannonball collision with target
            else if (cannonball.x + cannonballRadius > targetDistance &&
                    cannonball.x - cannonballRadius < targetDistance &&
                    cannonball.y + cannonballRadius > target.start.y &&
                    cannonball.y - cannonballRadius < target.end.y)
            {
                // determine target section number (0 is the top)
                int section =
                        (int) ((cannonball.y - target.start.y) / pieceLength);

                // check if the piece hasn't been hit yet
                if ((section >= 0 && section < TARGET_PIECES) &&
                        !hitStates[section])
                {
                    hitStates[section] = true; // section was hit
                    cannonballOnScreen = false; // remove cannonball
                    timeLeft += HIT_REWARD; // add reward to remaining time

                    // play target hit sound
                    soundPool.play(soundMap.get(TARGET_SOUND_ID), 1,
                            1, 1, 0, 1f);

                    // if all pieces have been hit
                    if (++targetPiecesHit == TARGET_PIECES)
                    {
                        cannonThread.setRunning(false);
                        showGameOverDialog(R.string.win); // show winning dialog
                        gameOver = true; // the game is over
                    } // end if
                } // end if
            } // end else if
        } // end if

        // update the blocker's position
        double blockerUpdate = interval * blockerVelocity;
        blocker.start.y += blockerUpdate;
        blocker.end.y += blockerUpdate;

        // update the target's position
        double targetUpdate = interval * targetVelocity;
        target.start.y += targetUpdate;
        target.end.y += targetUpdate;

        // if the blocker hit the top or bottom, reverse direction
        if (blocker.start.y < 0 || blocker.end.y > screenHeight)
            blockerVelocity *= -1;

        // if the target hit the top or bottom, reverse direction
        if (target.start.y < 0 || target.end.y > screenHeight)
            targetVelocity *= -1;

        timeLeft -= interval; // subtract from time left

        // if the timer reached zero
        if (timeLeft <= 0.0)
        {
            timeLeft = 0.0;
            gameOver = true; // the game is over
            cannonThread.setRunning(false);
            showGameOverDialog(R.string.lose); // show the losing dialog
        } // end if
    } // end method updatePositions

    // fires a cannonball
    public void fireCannonball(MotionEvent event)
    {
        if (cannonballOnScreen) // if a cannonball is already on the screen
            return; // do nothing

        double angle = alignCannon(event); // get the cannon barrel's angle

        // move the cannonball to be inside the cannon
        cannonball.x = cannonballRadius; // align x-coordinate with cannon
        cannonball.y = screenHeight / 2; // centers ball vertically

        // get the x component of the total velocity
        cannonballVelocityX = (int) (cannonballSpeed * Math.sin(angle));

        // get the y component of the total velocity
        cannonballVelocityY = (int) (-cannonballSpeed * Math.cos(angle));
        cannonballOnScreen = true; // the cannonball is on the screen
        ++shotsFired; // increment shotsFired

        // play cannon fired sound
        soundPool.play(soundMap.get(CANNON_SOUND_ID), 1, 1, 1, 0, 1f);
    } // end method fireCannonball

    // aligns the cannon in response to a user touch
    public double alignCannon(MotionEvent event)
    {
        // get the location of the touch in this view
        Point touchPoint = new Point((int) event.getX(), (int) event.getY());

        // compute the touch's distance from center of the screen
        // on the y-axis
        double centerMinusY = (screenHeight / 2 - touchPoint.y);

        double angle = 0; // initialize angle to 0

        // calculate the angle the barrel makes with the horizontal
        if (centerMinusY != 0) // prevent division by 0
            angle = Math.atan((double) touchPoint.x / centerMinusY);

        // if the touch is on the lower half of the screen
        if (touchPoint.y > screenHeight / 2)
            angle += Math.PI; // adjust the angle

        // calculate the endpoint of the cannon barrel
        barrelEnd.x = (int) (cannonLength * Math.sin(angle));
        barrelEnd.y =
                (int) (-cannonLength * Math.cos(angle) + screenHeight / 2);

        return angle; // return the computed angle
    } // end method alignCannon

    // draws the game to the given Canvas
    public void drawGameElements(Canvas canvas)
    {
        // clear the background
        canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(),
                backgroundPaint);

        // display time remaining
        canvas.drawText(getResources().getString(
                R.string.time_remaining_format, timeLeft), 30, 50, textPaint);

        // if a cannonball is currently on the screen, draw it
        if (cannonballOnScreen)
            canvas.drawCircle(cannonball.x, cannonball.y, cannonballRadius,
                    cannonballPaint);

        // draw the cannon barrel
        canvas.drawLine(0, screenHeight / 2, barrelEnd.x, barrelEnd.y,
                cannonPaint);

        // draw the cannon base
        canvas.drawCircle(0, (int) screenHeight / 2,
                (int) cannonBaseRadius, cannonPaint);

        // draw the blocker
        canvas.drawLine(blocker.start.x, blocker.start.y, blocker.end.x,
                blocker.end.y, blockerPaint);

        Point currentPoint = new Point(); // start of current target section

        // initialize curPoint to the starting point of the target
        currentPoint.x = target.start.x;
        currentPoint.y = target.start.y;

        // draw the target
        for (int i = 1; i <= TARGET_PIECES; ++i)
        {
            // if this target piece is not hit, draw it
            if (!hitStates[i - 1])
            {
                // alternate coloring the pieces yellow and blue
                if (i % 2 == 0)
                    targetPaint.setColor(Color.YELLOW);
                else
                    targetPaint.setColor(Color.BLUE);

                canvas.drawLine(currentPoint.x, currentPoint.y, target.end.x,
                        (int) (currentPoint.y + pieceLength), targetPaint);
            } // end if

            // move curPoint to the start of the next piece
            currentPoint.y += pieceLength;
        } // end for
    } // end method drawGameElements

    // display an AlertDialog when the game ends
    private void showGameOverDialog(int messageId)
    {
        // create a dialog displaying the given String
        final AlertDialog.Builder dialogBuilder =
                new AlertDialog.Builder(getContext());
        dialogBuilder.setTitle(getResources().getString(messageId));
        dialogBuilder.setCancelable(false);


        // display number of shots fired and total time elapsed
        // add the additional targetPiecesHit variable
        dialogBuilder.setMessage(getResources().getString(
                R.string.results_format, shotsFired, totalElapsedTime));


        dialogBuilder.setPositiveButton(   R.string.reset_game,
                new DialogInterface.OnClickListener()
                {
                    // called when "Reset Game" Button is pressed
                    @Override
                    public void onClick(DialogInterface dialog, int which)
                    {
                        dialogIsDisplayed = false;
                        newGame(); // set up and start a new game
                    } // end method onClick
                } // end anonymous inner class
        ); // end call to setPositiveButton

        activity.runOnUiThread(
                new Runnable() {
                    public void run()
                    {
                        dialogIsDisplayed = true;
                        dialogBuilder.show(); // display the dialog
                    } // end method run
                } // end Runnable
        ); // end call to runOnUiThread
    } // end method showGameOverDialog

    // stops the game
    public void stopGame()
    {
        if (cannonThread != null)
            cannonThread.setRunning(false);
    } // end method stopGame

    // releases resources; called by CannonGame's onDestroy method
    public void releaseResources()
    {
        soundPool.release(); // release all resources used by the SoundPool
        soundPool = null;
    } // end method releaseResources

    // called when surface changes size
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format,
                               int width, int height)
    {
    } // end method surfaceChanged

    // called when surface is first created
    @Override
    public void surfaceCreated(SurfaceHolder holder)
    {
        if (!dialogIsDisplayed)
        {
            cannonThread = new CannonThread(holder);
            cannonThread.setRunning(true);
            cannonThread.start(); // start the game loop thread
        } // end if
    } // end method surfaceCreated

    // called when the surface is destroyed
    @Override
    public void surfaceDestroyed(SurfaceHolder holder)
    {
        // ensure that thread terminates properly
        boolean retry = true;
        cannonThread.setRunning(false);

        while (retry)
        {
            try
            {
                cannonThread.join();
                retry = false;
            } // end try
            catch (InterruptedException e)
            {
            } // end catch
        } // end while
    } // end method surfaceDestroyed

    // Thread subclass to control the game loop
    private class CannonThread extends Thread
    {
        private SurfaceHolder surfaceHolder; // for manipulating canvas
        private boolean threadIsRunning = true; // running by default

        // initializes the surface holder
        public CannonThread(SurfaceHolder holder)
        {
            surfaceHolder = holder;
            setName("CannonThread");
        } // end constructor

        // changes running state
        public void setRunning(boolean running)
        {
            threadIsRunning = running;
        } // end method setRunning

        // controls the game loop
        @Override
        public void run()
        {
            Canvas canvas = null; // used for drawing
            long previousFrameTime = System.currentTimeMillis();

            while (threadIsRunning)
            {
                try
                {
                    canvas = surfaceHolder.lockCanvas(null);

                    // lock the surfaceHolder for drawing
                    synchronized(surfaceHolder)
                    {
                        long currentTime = System.currentTimeMillis();
                        double elapsedTimeMS = currentTime - previousFrameTime;
                        totalElapsedTime += elapsedTimeMS / 1000.00;
                        updatePositions(elapsedTimeMS); // update game state
                        drawGameElements(canvas); // draw
                        previousFrameTime = currentTime; // update previous time
                    } // end synchronized block
                } // end try
                finally
                {
                    if (canvas != null)
                        surfaceHolder.unlockCanvasAndPost(canvas);
                } // end finally
            } // end while
        } // end method run
    } // end nested class CannonThread
} // end class CannonView


7.1.    In the CannonController class, add the following statements and methods in the appropriate places.


papackage mdad.game;
import android.app.Activity;
import android.media.AudioManager;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;

public class CannonController extends Activity {
    private GestureDetector gestureDetector; // listens for double taps
    private CannonView cannonView;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cannon_controller);

        cannonView = (CannonView) findViewById(R.id.cannonView); // get the CannonView
        gestureDetector = new GestureDetector(this, gestureListener);
        setVolumeControlStream(AudioManager.STREAM_MUSIC);
    }

    SimpleOnGestureListener gestureListener = new SimpleOnGestureListener()
    {
        // called when the user double taps the screen
        public boolean onDoubleTap(MotionEvent e)
        {
            cannonView.fireCannonball(e);
            return true;
        } //
    }; // end gestureListener

    public void onPause()    {
        super.onPause();
        cannonView.stopGame();
    }

    protected void onDestroy() {
        super.onDestroy();
        cannonView.releaseResources();
    }


    public boolean onTouchEvent(MotionEvent event)  {
        int action = event.getAction();
        // the user user touched the screen or dragged along the screen
        if (action == MotionEvent.ACTION_DOWN ||action == MotionEvent.ACTION_MOVE)
        {
            cannonView.alignCannon(event);
        }
        return gestureDetector.onTouchEvent(event);
    }


}