Building a pdf programmatically on Android

In my recent project I needed to build a pdf and email it to a user. Android has a great library for this and I found it only took a few hours to get a reasonably readable pdf onto my screen.

 

The basic outline goes as follows


//create the pdf
<pre>PdfDocument reportDocument = new PdfDocument();
//create the page</pre>
<pre>PdfDocument.Page page = reportDocument.startPage(pageInfo);</pre>
<pre>
/*
* Do some stuff here. see further down
*/

</pre>
<pre>//end the page
reportDocument.finishPage(page);</pre>
<pre>//send it away to a file for storage or in my case an email for sending</pre>
<pre> pdfPath = getPDFFile(openProject.getProjectNme());
       FileOutputStream fos = new FileOutputStream(pdfPath);
        reportDocument.writeTo(fos);
        reportDocument.close();
        success = true;</pre>
&nbsp;

Now for writing stuff on the pdf itself

 


//I had  made a chart earlier and wanted it displayed
<pre>page.getCanvas().drawBitmap(charts[0], 5, graphy, paint)

//drawing some text on the page by setting up a paint object</pre>
<pre>    Paint paint = new Paint();
    paint.setColor(Color.BLACK);  
    paint.setTextAlign(Paint.Align.CENTER);
    paint.setTextSize(25);

//then writing</pre>
<pre>page.getCanvas().drawText("Source of Sound",width/2, titley, paint);</pre>
&nbsp;

 

Really this is a super hackey way of doing it. There must be a cleaner API or if i ever need to do this again I will write one. But it works so I guess Ill come back to that at a later time.

 

 

 

Complete code here.

<pre>private void makePDF()
{
    Log.i(TAG, "makePDF: started");

    String sHighestDec;
    String sAverageHighestDecibel;

    //region
    String weighting = "For the purpose of soundproofing a nuisance noise must be 10 decibel lower than the ambient decibels of ";
    String weighting2 = "the rooms future use. So an office/library of 40 decibels would need its soundproofing to bring exterior";
    String weighting3 = "nuisance noise to 30 decibels to not be noticed by end users. Frequently due to uncontrollable ambient ";
    String weighting4 = "factors it will be impossible to measure the new low of your target sound if it drop below 40 db";

    String outerLimits = "Remember this is app is a crude measurement and educational tool.";
    String outerlimits2 = "Your  device may not produce or record some sounds due to hardware limitations";
    String outerlimits3 = "Secondly, you may not be able to hear all range of sounds. Wear ear protection!";


    //endregion
    int width = 1224;
    int height = 1584;
    int left = 5;
    float titley = ((float)(height * .04));
    float titleOney = ((float)(height * .06));
    float titleTwoy = ((float)(height * .08));
    float graphy = ((float)(height * .1));
    float lineOney = ((float)(height * .55));
    float lineTwoy = ((float)(height * .57));
    float lineThreey = ((float)(height * .59));
    float lineFoury = ((float)(height * .61));
    float lineFivey = ((float)(height * .63));
    float lineSixy = ((float)(height * .65));
    float lineSeveny = ((float)(height * .67));
    float lineEighty = ((float)(height * .69));
    float lineNiney = ((float)(height * .71));

    PdfDocument reportDocument = new PdfDocument();

    //page 1
    PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(width,height,1).create();

    PdfDocument.Page page = reportDocument.startPage(pageInfo);

    Paint paint = new Paint();
    paint.setColor(Color.BLACK);

    //title of page
    Log.i(TAG, "makePDF: page 1");
    paint.setTextAlign(Paint.Align.CENTER);
    paint.setTextSize(25);

    //title
    String info1 = openProject.getProjectNme() + " " + openProject.getProjectLocation();
    page.getCanvas().drawText("Source of Sound",width/2, titley, paint);
    page.getCanvas().drawText(info1,width/2, titleOney , paint);
    info1 = openProject.getDate();
    page.getCanvas().drawText(info1,width/2, titleTwoy, paint);


    //chart
    page.getCanvas().drawBitmap(charts[0], 5, graphy, paint);

    paint.setTextAlign(Paint.Align.LEFT);
    //highest decibel and at what frequency recordedHighestAverage
    sHighestDec = "The highest decibel recorded was : " + String.valueOf(valuesChartOne[0]) + "db at approximately " + String.valueOf(valuesChartOne[1] + " hz");
    page.getCanvas().drawText(sHighestDec, left, lineOney, paint);

    //average decibel recorded during total running average
    sAverageHighestDecibel = "The average decibel recorded was : " + String.valueOf(valuesChartOne[2]);
    page.getCanvas().drawText(sAverageHighestDecibel, left, lineTwoy, paint);

    //outerlimits
    page.getCanvas().drawText(outerLimits, left, lineThreey, paint);
    page.getCanvas().drawText(outerlimits2, left, lineFoury, paint);
    page.getCanvas().drawText(outerlimits3, left, lineFivey, paint);


    reportDocument.finishPage(page);


    //page 2
    Log.i(TAG, "makePDF: page 2");
    if  (charts[1] != null) {
        pageInfo = new PdfDocument.PageInfo.Builder(width, height, 2).create();

        page = reportDocument.startPage(pageInfo);

        //title of page
        paint.setTextAlign(Paint.Align.CENTER);
        paint.setTextSize(25);
        page.getCanvas().drawText("Receiving Area", width / 2, titley, paint);

        //chart of recording
        page.getCanvas().drawBitmap(charts[1], 5, graphy, null);

        paint.setTextAlign(Paint.Align.LEFT);
        //highest decibel and at what frequency
        sHighestDec = "The highest decibel recorded was : " + String.valueOf(valuesChartTwo[0]) + "db at approximately " + String.valueOf(valuesChartTwo[1] + " hz");
        page.getCanvas().drawText(sHighestDec, left, lineOney, paint);

        //average decibel and at what frequency
        sAverageHighestDecibel = "The average decibel recorded was : " + String.valueOf(valuesChartTwo[2]);
        page.getCanvas().drawText(sAverageHighestDecibel, left, lineTwoy, paint);

        generateSTC();

        //if barrier what stl or not calculatable
        if (stcSuccess){

            String values = "Db lost : ";

            for (double d :
                    diff) {
                //Log.i(TAG, "makePDF: " + String.valueOf(d));
                int t = (int) Math.round(d);

                values += String.valueOf(t) + " ";

            }



            page.getCanvas().drawText(values , left, lineThreey, paint);
            page.getCanvas().drawText("Stc rating of this barrier is " + String.valueOf(stcRating), left, lineFoury, paint);

        }else{

            page.getCanvas().drawText("Stc rating was not successfully calculated", left, lineThreey, paint);

        }



        // weighting a sound
        page.getCanvas().drawText(weighting, left, lineFivey, paint);
        page.getCanvas().drawText(weighting2, left, lineSixy, paint);
        page.getCanvas().drawText(weighting3, left, lineSeveny, paint);
        page.getCanvas().drawText(weighting4, left, lineEighty, paint);

        reportDocument.finishPage(page);

    }


    //page 3
        if (charts[2] != null) {

            Log.i(TAG, "makePDF: page 3");
            pageInfo = new PdfDocument.PageInfo.Builder(width, height, 3).create();
            page = reportDocument.startPage(pageInfo);

            Material m = chosenMaterial.get(0);


            //title of page
            paint.setTextAlign(Paint.Align.CENTER);
            paint.setTextSize(25);
            page.getCanvas().drawText("Simulation", width / 2, titley, paint);
            info1 = m.getMaterialName() + " - " + m.getMaterialDescription();
            if (info1.length() < 100)
            {
                page.getCanvas().drawText(info1, width / 2, titleOney, paint);
            } else  {

                String one = info1.substring(0, info1.length()/2);
                String two = info1.substring(info1.length()/2 +1, info1.length());

                page.getCanvas().drawText(one, width / 2, titleOney, paint);
                page.getCanvas().drawText(two, width / 2, titleTwoy, paint);
            }


            //chart of recording
            page.getCanvas().drawBitmap(charts[2], left, graphy, null);

            paint.setTextAlign(Paint.Align.LEFT);
            //highest decibel and at what frequency
            sHighestDec = "The highest decibel recorded was : " + String.valueOf(valuesChartThree[0]) + " at approximately " + String.valueOf(valuesChartThree[1] + " hz");
            page.getCanvas().drawText(sHighestDec, left, lineOney, paint);

            //average decibel not being calculated


            //describe green and red marks
            info1 = "The green bars are "A" weighted to human perception based on frequency.";
            page.getCanvas().drawText(info1,left,lineFoury , paint);

            info1 = "The red dots are NC25 rating. (Allowable sound penetration for many use types)";
            page.getCanvas().drawText(info1,left,lineFivey , paint);

            reportDocument.finishPage(page);
        }



    boolean success = false;

    try {


       pdfPath = getPDFFile(openProject.getProjectNme());
       FileOutputStream fos = new FileOutputStream(pdfPath);
        reportDocument.writeTo(fos);
        reportDocument.close();
        success = true;

}catch (IOException f) {
        Log.e(TAG, "makePDF: ", f);
    }finally {

        if (success) {
            notifyObserver(1);
        }else   {
            notifyObserver(2);
        }


    }





}</pre>

The post Building a pdf programmatically on Android appeared first on SignalHillTechnology.

Powered by WPeMatico

Draw an animated bar chart on Android

How I drew a live bar chart in my sound proofing and STC app available on google play

Fast Fourier transform FFT android

 

The above picture is a screenshot of my android app where I needed to live display over 100 data points onto my screen. Here is how I did just that.

Important to know is that these data points came in the form of double array of [156] from my fast Fourier transform. With these 156 data point I needed to do the following;

  1. Draw a background and figure out how much of this data was needed to fill in my chart
  2. Grab that data asynchronously from a separate thread doing the calculations
  3. Take my data point and figure how wide and tall they need to be.
  4. Draw the previous record and then draw the new total over the top of it.
  5. Do it 30 times per second and post back to the UI

 

So let me start with item #2… how the data was getting there. The background thread that was giving me the 156 data points was refreshing very fast. I didn’t need all the data for the purpose of the user display and I chose to simply post it to an array repeatedly. This way it could post as frequently as it wanted and I could grab the data as frequently as a wanted without interruption to either. Loosely coupled might be the appropriate terminology.

 

This is my class wide field I posted too.


private double[][] uiChartBuffer;

This is what I call a controlling method. I have several inner classes within my audiocontrol class which do the work on different worker threads.
My controlling methods allow the classes utilizing the audiocontrol object to make its inner classes perform their functions without actually touching them. Here you
can see that I start recording and I instantiate the inner class drawchart and call its resume method. These inner classes are 100% accessed by their own controlling methods as well. It may
also be important to mention that the audiocontrol's controlling methods are affected by the android lifecycle. Killing the inner classes automatically.

So if the user gets a phone call and moves out of the app there are no leaked resources:
 Fragment.onPause() -> audioControl.onPause(this call a bunch of methods such as the one below) -> drawChart.pause and null -> recordAudio.pause() and null;
public void startRecording(File file)
{
 Log.i(TAG, "startRecording: ");
 running = !running;

 drawChart = new DrawChart(context);

 fileToWork = file;


 recordAudio = new RecordAudio();
 recordAudio.execute(getFile());


 drawChart.resume();

 


At the very bottom is my entire inner drawchart class. There is a resume and pause method, a method to check if the screen update is ready and a method that just loops over and over trying to update the screen.
Anything that doesn't have to do with the user interaction should be in a background thread. This is no exception however android doesn't allow background threads to touch the UI so you have to us a workaround.
In this case I used a surface view which is a very typical way of doing this.







</pre><pre>private class DrawChart extends SurfaceView implements Runnable
{
    SurfaceView surfaceView;
    private SurfaceHolder surfaceHolder;
    private Canvas canvas;
    private Paint paint;
    Thread thread = null;
    TextView textViewDecibelShow;
    int highestDeciebelFromSample;


    private int mBarColorF;
    private int mBarColorS;
    private Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.newhzdb);
    BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(),bitmap);



    private long nextFrameDue;
    private long frameLength = 100;
    int width, height;
    int barWidth, seqS, seqF,  mod;
    double eWidth, reverse, bHeight;
    float bh;


    public DrawChart(Context context) {
        super(context);

        surfaceView = myFragmentView.findViewById(R.id.surfaceView);
        textViewDecibelShow = myFragmentView.findViewById(R.id.textViewliveDB);
        surfaceHolder = surfaceView.getHolder();

        nextFrameDue = System.currentTimeMillis();



        mBarColorF = ResourcesCompat.getColor(getResources(),R.color.colorBarChartFluctuate,null);
        mBarColorS = ResourcesCompat.getColor(getResources(),R.color.colorBarChartStay, null);
        paint = new Paint();

        width = 0;



    }


    @Override
    public void run() {


        while  (running || playing)
        {

            if (thread.isInterrupted())
            {
                return;
            }


            if (updateRequired())
            {

                highestDeciebelFromSample = 0;

                if (surfaceHolder.getSurface().isValid() &amp;&amp; uiChartBuffer != null) {



                    canvas = surfaceHolder.lockCanvas();
                    // Log.i(TAG, "run: in midst of draws while loop");

                    if (width == 0){

                        width =  surfaceView.getWidth();    //screen dimensions 540
                        height = surfaceView.getHeight();   //screen dimensions 600

                        int almostwidth = (int) Math.round( width *.89) ;
                        barWidth = (int) almostwidth/reqBars;                       //4?
                        int netwidth = reqBars * barWidth;                         //464?
                        int grossWidth = (int) Math.round(netwidth/.90);            //515

                        width = grossWidth;



                        mod = getMod(width);
                        bHeight = height * .965;             // random number i found to keep max reading within chart
                        bh = (float) bHeight;

                        setDbChartScaleFactor(bHeight/myscale);

                        bitmapDrawable.setBounds(0,0,width,height);
                        //scaledBitmap = Bitmap.createScaledBitmap(bitmap,width,height,true);
                    }

                    bitmapDrawable.draw(canvas);





                    for (int i = 1; i &lt; reqBars ; i++) {

                        seqF = i * barWidth + mod; //right
                        seqS = seqF - barWidth; //left



                        int bottom = reverseandCalc(uiChartBuffer[0][i], false);

                        //Log.i(TAG, "run: " + String.valueOf(bottom));

                        if (bottom &gt; highestDeciebelFromSample){
                            highestDeciebelFromSample = bottom;
                        }




                        if (bottom &gt; highestDecibelByBar[i])
                        {// if this data is higher then add to record
                            highestDecibelByBar[i] = bottom;
                        }else {
                            //if not then draw prev record behind it

                            int top = (int) bh - highestDecibelByBar[i];

                            paint.setColor(mBarColorS);
                            canvas.drawRect(seqS, top, seqF, bh, paint);      //draw prev record redbar underneath
                        }


                        int top = (int) bh - bottom;


                        paint.setColor(mBarColorF);
                        canvas.drawRect(seqS, top, seqF, bh, paint); //draw green bar on top
                    }



                    surfaceHolder.unlockCanvasAndPost(canvas);


                }

                //averageDecibelForView = average(highestDeciebelFromSample);

                notifyObserver(3);

            }

        }

    }


    public void resume()
    {
        highestDecibelByBar = new int[reqBars];
        thread = new Thread(this);
        thread.start();
        Log.i(TAG, "resume: draw thread");

    }


    public void destroy()
    {
        thread.interrupt();
    }

    // TODO: 5/1/2018 update based not on time but on fft method calls
    public boolean updateRequired()
    {

        if (nextFrameDue &lt;= System.currentTimeMillis()){

            nextFrameDue = System.currentTimeMillis() + frameLength;


            return true;
        }
        return false;





    }





}
</pre><pre>
 

 


 

 

The post Draw an animated bar chart on Android appeared first on SignalHillTechnology.

Powered by WPeMatico

Getting Fast Fourier Transform data on Android

How I broke down the sound waves in my sound proofing and STC app available on google play

 

In order to build this app I needed to understand how loud a sound was as well as which frequency I was hearing. Truthfully when I started this app I underestimated how difficult this would be.

 

Building this app requires a few things;

 

  1. You need to sample the pressure of the airwaves hitting the microphone very quickly in order to build a sine wave. I found a great website with some images that included the following and this really gave me the basis of my understanding. Check out this guys website for more info here http://www.vlf.it/fft_beginners/fft_beginners.html

 

In order to accomplish that I built an android class called audio control. I built an interface so that fragments could get an instance of audio control and make sure to call all the methods needed in androids lifecycle.

Here are the fields at the top of the class;









<pre>//   my inner classes
private PlayAudio playAudio;
private RecordAudio recordAudio;
private TestTone testTone;

private AudioRecord audioRecord;
private AudioTrack audioTrack;

private RealDoubleFFT transformer;
// TODO: 4/18/2018 buffer size to 4096 so we narrow our frequency range down a bit?
private int SAMPLE_RATE = 11025;  //chnageable. lowest is 11025 according to newventuresoftware we have a gaurantee capture of 44100/ im dubious based on intial testing of 100 to 20k hz
private int blockSize = 256; // keep here number of component frequency samples that our transform object will output ...so is that 44100 /2 /256 = 86.132??
//but how because you only give it the raw input data
private int halfBlock = blockSize/2;
private int hzSpec;
private int reqBars =  0; //this reworks at 116? bars total
//int reqBars = 30;</pre>

 

Down below I have an inner class which extends Async Class









<pre>
private class RecordAudio extends AsyncTask<File, double[], Boolean>
{
    String TAG = "RECORD AUDIO";

    private static final int AUDIO_SOURCE = MediaRecorder.AudioSource.MIC;
    private static final int CHANNEL_MASK = AudioFormat.CHANNEL_IN_MONO;
    private static final int ENCODING = AudioFormat.ENCODING_PCM_16BIT;
    long startTime =0;
    long endTime = 0;
    FileOutputStream waveOut;



    @Override
    protected Boolean doInBackground(File... files) {

        try {

            dbAverage = new int[smooth];
            recordedHighestAverage = 0;
            totalRunningAverage.clear();
            int minBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_MASK, ENCODING);
            audioRecord = new AudioRecord(AUDIO_SOURCE, SAMPLE_RATE, CHANNEL_MASK, ENCODING, minBufferSize);

            if (files[0] != null) {
                waveOut = new FileOutputStream(files[0]);
                writeWavHeader(waveOut, CHANNEL_MASK, SAMPLE_RATE, ENCODING);
            } else {
                waveOut = null;
            }

            int bufferReadData;
            byte[] buffer1 = new byte[blockSize];
            long total = 0;

            try {
                startTime = SystemClock.elapsedRealtime();
                audioRecord.startRecording();

            } catch (IllegalStateException e) {

                Log.e(TAG, " Records doInBackground: " + e.toString());
            }


            while (running) {
                //Log.i(TAG, "while");

                bufferReadData = audioRecord.read(buffer1, 0, blockSize);  //we are requesting 256 byte obj and android is sending us 16bit so each byte hold half of a 16 bit!!


                publishProgress( createFFT(bufferReadData, buffer1));




                if (files[0] != null) {

                    createWavFile(total, bufferReadData, buffer1);
                }


            }

        } catch (IOException e) {
            Log.e(TAG, "Records doInBackground: " + e.toString(), e);
            stoprecording();
        } finally {

            Log.i(TAG, "Records doInBackground: calld from 2nd");

            endTime = SystemClock.elapsedRealtime();

        }

        if (waveOut != null) {

            try {
                updateWavHeader(files[0]);

            } catch (IOException e) {
                Log.e(TAG, "doInBackground: ", e);

            }
        }</pre>

 

Notice above where I publish progress to my FFT method. The above code merely show you how to sample the sounds from the air and get them into a byte array for more processing. Next we need to do some data manipulation and get the Fourier Transform and Decibel information.

I found a great website that explains that a sine wave is merely a circle with time expressed as well. Great presentation on their part and although I don’t completely feel I have internalized the lesson it has definitely improved my understanding. https://betterexplained.com/articles/an-interactive-guide-to-the-fourier-transform/

 

I ended up downloading the FFT pack that is free and open source on the internet. You can download it by searching   ->    ca/uol/aig/fftpack

Add it as a library and then use the FFT as follows:

Initialize at top of class above all your methods.

<pre>private RealDoubleFFT transformer;</pre>

 

Here is the method called by our background thread gathering the audio data. As you can see I needed both bytes and short in this software and stumbled through the process of making them as seen in my little sketch below which was my reference point.

Notice below the trasnformer.ft() method is doing all the FFT work. Everything before that is just cramming data into a 16 bit configuration. Or getting me my decibel value.

 

<pre>
protected double[] createFFT(int bufferReadData, byte[] buffer1)
{
    double[] toTransform = new double[blockSize/2];
    double sum = 0;
    int block = halfBlock;
    double REF = 0.00002;
    //ByteBuffer.wrap(buffer1).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(buffer);   //this hopefully creates the buffer1 array

    // xx is correct bits. Here we load byte from buffer lilendian to big to feed into the newBuff
    //  xx xx xx xx 00 00 00 00
    //  00 00 00 00 xx xx xx xx
    //  xx xx xx xx xx xx xx xx
    // 0xFF added to fix a left padding problem??

    short newBuff, buffLil, buffBig, count = 0;

    for (int i = 0; i < blockSize/2 && i < bufferReadData/2; i++) //after 128 runs with a double increment it is pulling what data?
    {

        buffLil = buffer1[count];
        buffBig = buffer1[count + 1];
        newBuff = (short) ( buffBig <<8 | buffLil & 0xFF);
        //Log.i(TAG, "createFFT: short " + String.valueOf(newBuff));
        count ++;
        count++;

        /* testing new stuff here */
        if (newBuff > 0) {

            sum += Math.abs(newBuff);

        }else
        {
            block--;
        }


        toTransform[i] = (double) newBuff / 32768.0;             //This takes the short and divides by total short value to give a decimal double value between -1.0 to 1.0 for input into fft

    }


    double x = sum/block;
    //Log.i(TAG, "createFFT: average amplitude " + String.valueOf(x));

    double db = 0;

    if (x != 0) {

        double pressure = x/51805.5336;

        db = (20 * Math.log10(pressure/REF));

        if (db > 0){

            //Log.i(TAG, "createFFT: " + String.valueOf(db));
            int d = (int) db;
            //Log.i(TAG, "createFFT: db" + String.valueOf(d));
            averageDecibelForView = average(d);
        }




    }

    transformer.ft(toTransform);


    return(toTransform);


}</pre>
&nbsp;

 

You may also notice that I am calculating my decibels based on the guesstimated microphone pressure values someone provided me. You may also noticed That I push those into the average() method.

This fft was running very fast despite the math required and I was only refreshing the screen about 30 times per second so not all the data needed to hit the screen and surely the user didnt need to see the decibel amplitude 30 times per second.

Below you can see the result. I will get into drawing the display on my next post but it shows the current decibels top left and moving bar chart below.

 

Fast Fourier transform FFT android

 

 

 

The post Getting Fast Fourier Transform data on Android appeared first on SignalHillTechnology.

Powered by WPeMatico

Sound Proof STC APP – Available for android on google play store

The essence of construction if building a barrier between us and the elements of nature or often, just as importantly, other annoying people.

But how do we do that relating to sound?

 

In the late 60 sound proofing became a more examined topic in construction. It continues to evolve today but very little of that evolution is in the hands of the end user and installer. So despite the importance of sound proofing construction decisions are frequently left to ” Well, this should work” … But will it?

One of the main barriers is the lack of knowledge by both sound engineers in how to get more information to contractors and contractors in understanding how sounds travel through buildings.

 

I have a lot of respect for sound engineers and testing agencies but…

They fail to apply their knowledge to real world situations. Testing in a laboratory is great. It’s accurate and provides crucial research and data. In that same vein taking a leer jet is much faster and safer than driving a car. But the reality is cars get more people more miles every year than leer jets do.

In much the same way the average person can’t stand around waiting for their leer jet to arrive we cannot stand around depending on these tests to make practical use of the information collected. The average property owner or builder needs something cheap and reasonably effective yesterday.

 

I have a lot of respect for contractors and designers but…

Frequently they are guessing. They know they can get someone to analyze acoustics and sometimes they do. But most of the time they use their ears make some assumptions and just start building stuff until the sound gets to a tolerable level.

 

I designed this app to give people making decisions every day something to help in that decision making process.

To allow people to quickly grab some data for review later, or to take someone with zero knowledge and help them make a better informed decision.

Is it perfect…no. But its better than nothing.

 

Below are a few screen shots. Check it out on google play.

 

 

sound proof and stc app android

sound proof and stc app android

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

sound proof and stc app android

sound proof and stc app android

Powered by WPeMatico

Project Recaps

Interested in some recent projects completed?

 

Laboratory

Long Beach, Ca

This project was local and was a cinch to handle. Turning a torn up commercial mixed use building into a laboratory.  The scope of work included a new electrical service, architectural repairs, cabinetry, rooftop HVAC units and other amenities associated with a laboratory. Here is a 4 picture snapshot of start to finish.





 

A picture is worth a thousand words when communicating with multiple parties to overcome obstacles. Yes, I have a thermal camera and yes your jealous.

 





 

 

Warehouse

Long Beach, Ca

Another day, another flip. Freshen up the office and warehouse. Demo the mezzanine. Install some security features. Rinse repeat!

 




 

 





 

 

Professional Office

Los Angeles, Ca

Retail into a professional office.





 

 

Retail Strip Mall

Lomita, Ca

Fixing up a store front strip mall. Painting, roofing signage and other improvements.

 





 

New offices

Los Angeles, Ca

Large open office into private offices.




 

Warehouse Remodel with New Offices

Vernon, Ca

Offices from the beginning of time. Everything must go!





 





 

 

Gym at Retail Strip Mall

Orange County, Ca

Empty space to gym.

building a gym

building a gym

gym metal stud framing

gym metal stud framing

gym construction complete

gym construction complete

gym construction drywall texture

 

 

Powered by WPeMatico

Easy Time Card App – The paper time card alternative

Many offices today are still filling out paper time cards. Why you ask?

2018-03-06 11.12.29

  1. Nefarious software integration
  2. The learning curve
  3. Bugs or “features”

Almost everyone is guilty of it. Software products are designed to work with constant support resulting in monthly or yearly fees. Upgrades, server costs etc. But most people aren’t getting paid to figure out software. Many low skill employees can’t or drag their feet. They want the solution that stays the same. Not one that changes with the weather. As they sort through all the numerous buttons they passively fear that the next upgrade will change things and they will spend more time relearning. Barf.

 

But paper time cards suck!

Paper time cards take time to fill out. There is zero space to write. They get smudged and lost plus people have to hand deliver them back to the office. Someone needs to sort through them. It’s not really a great solution.

 

A better solution

For sale on google play store is the EASY TIME CARD APP. A compromise between the two.  This app is real easy. Two buttons to operate it and if the setup is too hard someone else can do it once and its done. Right is a screen shot from the main screen of the app. You can see the current day being shown as clocked in at the top.

 

At the bottom, when the user is ready to clock out just press the big red button.

 

When the user is ready to report their time they can press the button on the bottom  labeled report time. This send the time card over in an easily readable email without any work.

 

 

2018-03-06 10.57.43

Here you can see the email which is automatically made after the user clicks the “Report Time” button.

It super clear and easy to read. It tracks starting and ending times as well as total work times.

As workers go from job to job they can track how much time they are spending at each location or task.

Best of all the records can be emailed to three people. So now that single time card can be reviewed and saved digitally.

Workers are still on the honor system because these time card emails can be edited manually. This is helpful is you make a mistake and forget to clock in. Simply type in a note on the email or change a start time.

Most importantly this is something 99% of people already know how to do. This app isn’t trying to change your life. Its simply the exact same paper time cards you have relied on in a digital format!

 

Powered by WPeMatico

Foot Candle Light Meter

The foot candle light meter is one of the first apps developed. Very simple yet handy for construction professionals.

Most, if not all, smartphones have an ambient light sensor which is used to control screen brightness. This hardware is designed and programmed by the manufacturer. So its very hard for me as a consumer and developer to analyze the accuracy of these devices. After all, the hardware interacts with the android system and I am served up with a number defined as lux which is an SI unit.

In order to make this app work I used Java and a lot of the boilerplate code needed for android.

But in essence the app boiled down to a single method.

 

Does it get any easier. No, its as close to the “Hello World” of scientific apps as you can get. However it does serve a purpose for the consumer.

 

Building codes throughout north america use foot candles as a measure of describing illumination. Although antiquated everyone still uses it and many product labels and building codes are described in foot candles. This app allows the average person to make a fairly accurate approximation about their existing lighting or where their lighting needs to be for the task required.

Above you can see the layout is simple. That’s the foot candles on my office desk with the lights on full blast!

 

The post Foot Candle Light Meter appeared first on SignalHillTechnology.

Powered by WPeMatico

Commercial property construction as an investment

 

When performing construction work to a commercial property it only makes sense to consider these expenses as an investment.

  • Will that investment pay off?
  • Are there other, better, investment opportunities?
  • Will I be able to get this same work later for more or less expense?
  • Will delaying this work accrue extra damages if not performed promptly?

 

These are some of the important questions a property owner needs to consider. At soco construction your concerns make a lot of sense. Many contractors will explain that investing in your building is always a good idea. But I would like to talk about why this is not necessarily true. To break this down better lets divide this discussion up into three major factors affecting your work.

  1.  The scope of work
  2. The quality of the work
  3. When and how fast

 

The scope of work is a key consideration. If you are a an end user the scope of work might be dictated by the business. But that doesn’t mean you can’t save money! Frequently innocent and relatively small architectural mistakes lead to big cost increases. If your project is over 50 thousand dollars in size and you don’t have a contractor you use regularly it would be wise to pay a contractor for a constructability review. A few hundred dollars could save you thousands down the road. Furthermore it may be wise to pay a consultant for a price estimation on the plans. Often times contractors will submit very different prices. However after choosing the less expensive option a customer can find out that a major scope of work was left out and that cheaper option was actually more expensive and less experienced! Lastly if you are a landlord or other type of non end user you have greater simplicity and flexibility. Most tenants prefer core building features such as electrical systems and high security doors to be functioning. They would also typically prefer a credit for the cost of painting the building to do with as they please than to have the building painted.

 

Secondly is the quality of the work. This is a double edged sword as I will explain. Frequently customers will focus on an end product with little concern for sustainability. For example installing an expensive tile over a terrible substrate. The install will look poor and will fail very soon only to be redone at a greater cost. It would have been better to pay twice as much for a job that would last 10 times as long. Or building an expensive new office around a failing electrical or plumbing system. In these examples the customer will achieve short term results but will pay a far greater cost in the long term. The other side of this is putting too much money into something that nobody cares about. To rehash the tile example someone once paid soco construction to install a very expensive tile in a not so great warehouse office despite our suggestion of a sealed concrete floor. The tenants moved in and filled the office with boxes and furniture and there it sits completely covered up. Frequently fixtures and finishes used by tenants will be quickly worn down. Time and time again customers request expensive commercial toilet paper dispensers and paper towel dispensers. But after the lease is expired I come back to find them all broken and the paper products resting on the fixtures. Why not install the simpler cheaper and more attractive versions? For most people they just don’t know any better.

Lastly is when and how fast. Unfortunately the best time to invest in your building is also the best time not to invest in your building. When rents are high and opportunity seems abundant contractors are busy and expensive. When work is slow for contractors it benefits both you and the contractor to keep a steady work flow. Prices are lower and availability is better. However it is during these times that you can frequently make good investments in other property or the the stock market for example. Don’t just invest in your property without thinking. If your money can be put to use elsewhere do it. Wait until the timing is right. Also there is a certain value to be had by planning ahead. Many people will wait till the last minute to contact someone about their project. Then they will set moronic bid dates that squeeze everyone’s time or make unreasonable deadline for finishing the project and therefore drive up the price. Now obviously, there may be other financial circumstances driving these decisions. But countless times customers call and ask for some arbitrary work to be done right away or in a meeting a group of corporate executives will sit there and talk about the project for 3 months and they pay through the nose during the 3 months of construction timeline as everything is rushed to the job instead of working themselves harder to make decisions in the first month and give the contractor 5 months. It benefits the customer to call early and ask the contractor for a price and let them know there are no major time constraints.

Powered by WPeMatico

Crossfit Gym Construction

This was a gym construction in which I did the majority of the work and the owner pitched in or used friends for certain things he could get done at the friend price.

Here is a picture of day 1. Someone else did the demo and we just showed up to take a look around.

building a gym

building a gym

Our starting point was to get some underground plumbing done. We had an existing nearby restroom and are merely moving it over about 25 feet.

building a gym

building a gym

We had to relocate some mechanical lines. Material was delivered right away as well.

building a gym

building a gym

Here we had some windows being covered by a wall. So we painted some drywall black and attached it into the window frame to obscure the view from outside.

blacking out window with drywall gym construction

After we reworked the mechanical and poured the underground we started framing.

gym metal stud framing

gym metal stud framing

gym construction metal stud demising wall

 

Here the panel has a bunch of new electrical runs hanging from as we still work.

gym construction electrical

After we double drywall and insulate we are spraying some texture. Some walls are fire tape only because they will be covered with wood.

gym construction drywall texture

Rear wall fire tape

 

Don’t forget to patch the roof!

crossfit gym construction roof patch

The owner and his buddies put in a lot of the finishes such as the high bay lighting, the foil, the paint and the wood. Which is fine with me. Here is the finished product. I think it looks great.

gym construction complete

gym construction complete

 

gym construction complete

gym construction complete

 

 

Powered by WPeMatico

Waterproofing second floor restroom

2016-06-24 13.40.45

This job was in Carson a. A customer from a previous job asked me to waterproof a new bathroom installation because of a leak that had gone into the IT room below.

Whenever someone installs a toilet there is a question of whether to caulk or silicon around the base. Here is my two cents. On a first floor concrete installation you should always caulk or silicon around the base of the toilet. This isn’t necessary but it looks nicer and if the ring ever fails you have a second line of defense.

On a wooden sub floor you should never caulk or silicone around the base because if the ring fails water will be trapped and hidden in the sub floor and will cause damage even if there is water proofing. End rant.

2016-06-24 12.59.43

On this job there was a lightweight concrete deck on top of a plywood sub floor. In order to water proof this we used a product called hydro ban. Hydro ban will waterproof a solid surface by merely painting it onto the surface. Judging by the condition of the existing concrete I could determine there was no cracking or deflection in the floor. If the floor is flexing this need to be corrected before attempting to waterproof.

2016-06-24 12.57.55

A plumber had just installed floor drains before we came. It was obvious that any water leakage was around the toilet flange or where the floor meets the wall as naturally there is a small seam at these points. We used a cloth fabric designed for water proofing to cover these points plus the new floor drain and create a completely solid surface to water proof. Then we gave the floor two coats of water proofing material.

Now all the need to be done is too make sure the tile installers do no carelessly or purposely scratch the floor when installing the tile.

This method will stop minor flood or leaks. Obviously if water runs out from the restroom into the hallway the water proofing stop at the threshold of the restroom door.

2016-06-24 13.42.52

Powered by WPeMatico