Showing posts with label Android App. Show all posts
Showing posts with label Android App. Show all posts

Monday, September 7, 2020

Connect to Web Socket From Your Android App using OkHttp

Hello,

In this blog I am going to explain how you can connect with Web Socket from your Android App using OkHttp. This will be useful when you want to send some data real time from your Android app to Web app. 

First add following line in your build.gradle file dependencies. 

implementation 'com.squareup.okhttp3:okhttp:3.11.0'

Now let the gradle build and sync. Next we will make a class which extends WebSocketListener from OkHttp3 and a new class and name it MyWebSocketListener.java and add following code to it. 

import okhttp3.Response;

import okhttp3.WebSocket;

import okhttp3.WebSocketListener;

import okio.ByteString;

public final class MyWebSocketListener extends WebSocketListener {

    private static final int NORMAL_CLOSURE_STATUS = 5000;

    public MyWebSocketListener() {  

    }

    @Override

    public void onOpen(WebSocket webSocket, Response response) {

        webSocket.close(NORMAL_CLOSURE_STATUS, "Exit");

    }

    @Override

    public void onMessage(WebSocket webSocket, String text) {

        //String Meesage received

    }

    @Override

    public void onMessage(WebSocket webSocket, ByteString bytes) {

        //Meesage received in form of binary data

    }

    @Override

    public void onClosing(WebSocket webSocket, int code, String reason) {

        webSocket.close(NORMAL_CLOSURE_STATUS, null);

        //Closing socket

    }

    @Override

    public void onFailure(WebSocket webSocket, Throwable t, Response response) {

        t.printStackTrace();

    }

}

Now we have listener created. Let's create client and attach it to Web Socket and Listener.

val listener = MyWebSocketListener()

val webSocket =   OkHttpClient.Builder().build().newWebSocket(request, listener)


That's it, now you can use this instance to send or receive message.

Tuesday, August 4, 2020

Android Build Error - The Crashlytics build ID is missing

Hello,

Recently I faced a very strange issue in one of my android app. We were using Fabric IO for crash analytics earlier in the app and since now it's already merged with Firebase Analytics. we need to update the app. 

I removed Firebase maven repository and removed all plugins from build gradle file and removed Fabric code from everywhere. And build was successful. After that I followed all the instructions on getting started page of Firebase and crash analytics and build was successful.  And then problem started. Once we run the app, it crashes and shows following error in Logcat. 

"The Crashlytics build ID is missing. This occurs when Crashlytics tooling is absent from your app's build configuration. Please review Crashlytics onboarding instructions and ensure you have a valid Crashlytics account

Now it seems there is an issue with Firebase crash analytics account so I regenerated the google service json file add updated in project. But it didn't help. 

Searched on Google, answers suggest to put back Fabric plugin again in gradle file. Which does not make any sense to me. Since Fabric is out dated, why do we need to add it again. 

Finally after 2 days I was able to solve the problem. Here is the solution

Remove google service json file. Use Firebase assistant and connect with your account again. Select the project and it again generate google service json file. Keep link to fabric io repository in top level gradle file.

maven { url 'https://maven.fabric.io/public' }

I don't know how it worked but it works and I really don't know the reason of why it didn't work and why it started working after above solution. But it does solve the problem. 

If anyone who is reading this blog, knows the reason please put in comment. 

Sunday, March 22, 2020

Socket.io Not Working on Android 9 (API 28)

Hi,

Recently in one of the project we faced a situation where we have used socket.io on backend and Android app connects to it. Socket.io was using http protocol for testing. It worked fine in older android versions but faced an issue in Android 9 and above where socket was connected but emit was not getting on server.

After couple of hours struggle finally found the problem. The problem was socket io was using http protocol and it's clearText HTTP request and it is banned in recent android version considering security reasons.

In earlier android version by default clearText is set as true but in later version default value is false. That means android components like HTTP and FTP and classes DownloadManager, and MediaPlayer can not use HTTP.

To over come this issue and allow socket to connect with HTTP protocol you have to allow clearText traffic on app level and for that you have to add following attribute in Application tag of your AndroidManifest.xml

<application
        android:usesCleartextTraffic="true">
.....
</application>

Once you add this to your manifest file, your socket emit will start working and you can still use HTTP with your socket.

Please note that since it was testing app we used HTTP based socket. However in production you should always use HTTPs protocol.

Hope this helps you.


Saturday, February 11, 2017

Effective Way to Create and Save File In Android Application

Hello, in this blog post I am going to explain effective way of creating and saving file in android application. Also will explain what are the general mistakes developer make in creating and saving file and how to prevent it.

Why it is important to have strategy for File create and Save in Android?



Because android file system is very similar to disk based file system and when there is a problem in creating or saving file it throws IOException so your application may crash while it's running and it's not good for professional or business application. So you need a proper strategy to create and save file and you have to add all kind of checks and exception handling for this.


Common mistake by developers on Creating and Save file in Android


Any new developer who is working first time on the file make this mistake. They don't know how to create a file and they do not refer to android developer site and android SDK document. They just search on web and find answers on site like StackOverFlow and copy paste the code. But sometime what happens is the code which they have used was about saving file in SD card and it may be possible that some phones do not have SD card so when the application is running in these phones, it crashes.

Another mistake is, in the code they hard code the path of saving file. Each android version and phone has different way of handling SD card so if you hard code the path it may work in your phone but it does not work in other phone. So here you need some strategy for this and make sure that your code works in all scenarios.  Here are some common things you should consider while creating and saving file.


  1. Decide where you want to store file, in external storage or internal storage.
  2. If storing in internal storage make sure there is space available.
  3. If storing in external storage, first check if there is external storage is available and there is a space.
  4. Make sure you have permission to store files in external storage. 

If you have requirement that file created should only be accessed by your app, you should create it in internal storage in app directory or else you should create it in external storage. Here is the code I have used in one of my app to create file in external storage.

For this you should also have permission mentioned in your android manifest file.

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


public static final int MEDIA_TYPE_IMAGE = 1;
public static final int MEDIA_TYPE_VIDEO = 2;

private static File getOutputMediaFile(int type){
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.

File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
 Environment.DIRECTORY_PICTURES), "YOURAPP");

// Create the storage directory if it does not exist
if (! mediaStorageDir.exists()){
if (! mediaStorageDir.mkdirs()){
Log.d("YOURAPP", "failed to create directory");
return null;
}
}

// Create a media file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File mediaFile;
if (type == MEDIA_TYPE_IMAGE){
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"IMG_"+ timeStamp + ".jpg");
} else if(type == MEDIA_TYPE_VIDEO) {
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"VID_"+ timeStamp + ".3gp");
} else {
return null;
}
return mediaFile;
}

As you see in above code, we are first checking if external storage is available or not. If it's not available, you just return null and if it's available, depending on file you want to create, create unique file name with current time stamp. Do not use hard coded file name like myVideo.3gp and it will always overwrite the previously created file.

Now we will use above function to check if we have file created or not in external storage and if not created we will create it in internal storage.

try{
File outputFile = getOutputMediaFile(MEDIA_TYPE_VIDEO);

if(outputFile == null){
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
outputFile = new File(getFilesDir() + "/"  + File.separator +
"VID_"+ timeStamp + ".3gp");
}
} catch (IOException e) {
Log.d(TAG, "IOException creating file : " + e.getMessage());
}

As you see in above code we are calling this function to create file of type video and check if it returns null. If it returns null then we are creating file in internal storage. Following line creates file in internal directory.

outputFile = new File(getFilesDir() + "/"  + File.separator +
"VID_"+ timeStamp + ".3gp");

And we have enclosed code try catch block to catch any possible IOException. In case of exception, you should properly display a message to user.

Friday, February 3, 2017

Android SDK Content Loader failing with NullPointerException

I have just recently started getting an error any time I interact with the Android SDK and AVD Manager in Eclipse. It does not load SDK manager and when I try to open AVD manager it says please set "Android SDK path in preference". This was strange error I started getting since I added few AVDs with Google API and when I check log of NullPointerException it was showing error in AVD Info class so I was sure that error is related to AVD. May be I misconfigured AVD while creating and now it's not allowing me to do any work in Eclipse.

So to solve this error you have to delete .android directory located in your user profile folder if you are using Windows and if you are using Mac, this will be hidden directory in your home directory.

So first of all make all the hidden directory visible. First of all open terminal and type following commands.

defaults write com.apple.Fider AppleShowallFiles YES

Now you have to kill all the finders and then go to home directory.

Killall Finder

Now when you go to home directory you will see .android directory which was hidden.


Delete that directory and even delete it from trash.

Now restart the Eclipse and try to load AVD manager and Android SDK manager and it should work fine.

NOTE : Please note if you delete .android directory, you will loose all the AVD you have created before and some of your preference of eclipse, so please use it carefully. 

Monday, January 2, 2017

Android MediaRecorder Stop Failed - 1007

If you have ever used Android MediaRecorder to record video or audio in your android application, you must have faced this error. Recently I had nightmare in using MediaRecorder in my android application. I think it's most unstable class in Android SDK and there is a different behavior in each device. In this blog I am going to explain more about this and possible resolution of this.


First lets see wha's the meaning of this error : Android MediaRecorder Stop Failed - 1007

There is no documentation on Android developer site about this error code but from my experience I found out that there are three reasons behind this error

1) You called stop method too early.

According to documentation on MediaRecorder.java class it says

Stops recording. Call this after start(). Once recording is stopped, you will have to configure it again as if it has just been constructed. Note that a RuntimeException is intentionally thrown to the application, if no valid audio/video data has been received when stop() is called. This happens if stop() is called immediately after start(). The failure lets the application take action accordingly to clean up the output file (delete the output file, for instance), since the output file is not properly constructed when this happens.

That means if you start recording and in couple of seconds you call stop recording there is no significant data of recording hence stop method throws an exception. So you must wait for sometime. From my testing I found out that you should at least for minimum 10 seconds. So the solution is to have stop method called inside try catch block and handle the exception properly or do not allow user to stop recording till 10 seconds of start of recording. You can either disable stop button and make it enable after sometime. Once the exception is thrown, you should clean the the resources, release media recorder and camera and all other objects.

2) Error in Writing to Output file

We have to set output file to MediaRecorder when we start recording. When you stop all the recorded data will be appended in output file specified by you on start. This could failed due to reasons like insufficient space to write file or you don't have permissions to write to external and internal storage due in Android 6.0 on words.

3) MisMatch in preview size and video size set in MediaRecorder

I have mentioned about this in my previous blog. You can read it here.

Android MediaRecorder Start Failed - 19

Hope this helps you and save you time.

Friday, December 30, 2016

Android MediaRecorder Start Failed - 19

If you have ever used Android MediaRecorder to record video or audio in your android application, you must have faced this error. Recently I had nightmare in using MediaRecorder in my android application. I think it's most unstable class in Android SDK and there is a different behavior in each device. In this blog I am going to explain more about this and possible resolution of this.

First lets see wha's the meaning of this error : Android MediaRecorder Start Failed - 19

There is no documentation on Android developer site about this error code but from my experience I found out that it's because of mis match in size of video preview frame and MediaRecorder video size and this error comes randomly in different devices. The best approach is to use CamCorder profile as mentioned on Android developer site.

CamcorderProfile profile =CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
mMediaRecorder.setProfile(profile);

When you use this most of the following default values are set in MediaRecorder from the selected profile.


  • OutputFormat
  • AudioEncoder
  • VideoEncoder
  • VideoSize
  • VideoFrameRate

But this does not work on all the devices as some the devices don't accept these default values hence MediaRecorder does not work and specifically due to VideoSize it gives error. So to solve this you should manually set all the values. Following settings works on almost all devices

int width = mCamera.getParameters().getPreviewSize().width;
int height = mCamera.getParameters().getPreviewSize().height;

mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
mMediaRecorder.setVideoSize(width, height);
mMediaRecorder.setVideoFrameRate(30);

However there are still some problem with it. When I tested in some high resolution devices. I got following width and height from getPreviewSize()

2048
1536

But MediaRecorder failed to start and when I checked error log I got error.

Unsupported Video Dimension 1920 X 1688

That means it changed the dimension by itself and ignored the width and height I set it in setVideoSize. So that means you can not depend on it. So what's the solution for this. Well the solution is find the optimal video size based on your preferred frame dimensions and available supported size in your device. Following is the function I used for it.

private Size getOptimalPreviewSize(List sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.2;
double targetRatio = (double) w / h;
if (sizes == null) {}
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for (Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}


List sizes = mCamera.getParameters().getSupportedPreviewSizes();
Camera.Size optimal = getOptimalPreviewSize(sizes, 640, 480);

if(optimal != null){
           
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
mMediaRecorder.setVideoSize(optimal.width, optimal.height);
mMediaRecorder.setVideoFrameRate(30);      

}else{

int width = mCamera.getParameters().getPreviewSize().width;
int height = mCamera.getParameters().getPreviewSize().height;

mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
mMediaRecorder.setVideoSize(width, height);
mMediaRecorder.setVideoFrameRate(30);      

}    

For me above solution worked on almost all devices, first we get optimal size from available supported sizes and if there is no supported sizes then we use default preview size.

Friday, December 9, 2016

Digital Brochures For Builders, Real Estate Developers

World is going towards digitization but Real Estate Sector is still not using digital technology at it's best. They are still following the old approach of printed brochures to show their development informations and all. This is not just costing them much but this is no more a convenient way.

When I have a smart phone in my hand why should I carry printed, bulky brochure, that can be mis placed or won't be able to give me all the information I need. Instead of that if we have digital brochure, you can just have it in your smart phone. You can check it anytime, it does not consume much space and will be available anytime.


For the digital brochures, a mobile application is the best way to deliver. If you are in real estate business, then having mobile app for your projects will be one of the best way to market your projects and get more customers. A mobile application with all the features to showcase your project can boost your sales and customer engagements.

Mobile application can easily be downloaded and installed in phone. Your customer can find it and can see your projects. You can send notifications to customers for your upcoming projects. You can show entire gallery of your projects. You can show your project location on map, so customer can see exact location and app can guide them to reach at your project easily instead of manually finding a way.

I will give you more information by giving an example of the app we created for leading real estate developer of Gujarat.

Recently we created Digital Brochure Mobile Application for KP Sanghvi Infra for show casing their projects. I will give more informations about the features we added in the app. You can check application on Google Play Store


1) Show your projects according to category like Residential and Commercial



2) Show list of the projects according to selected category.



3) Let the user filter it based on project status and city



4) Show All the Information about selected project.


5) Show Project Gallery with filter like Floor Plan, 3D Photos etc, Since it's digital photographs, there is no limit.



6) Show Exact Project Location on Map and Guide user to come to your location easily.




So with this approach, your customer will have all your information in their. They can check and share it with others anytime. You can send notifications to customers. 

So go digital with this solution, if you need one contact us at.

Email : info@thedesignshop.co.in

Tuesday, December 6, 2016

Android SDK Missing Google Play Services In Revision 30

Recently I faced an issue with my Android SDK, I updated Google Play Services to Revision 30 using SDK manager from eclipse and after update sdk/extras/google/google_play_services/libproject folder was missing and all of my projects gave me error where I have added google play services lib was added.

Now this is bit strange. I checked my SDK folder and find out that instead of 

sdk/extras/google/google_play_services/libproject 

We now have

/sdk/extras/google/m2repository/com/google/android/gms

Where we have play services folder, inside which there are various version folders and it has .aar and .pom file. 

I was not sure how to use that with Eclipse. One option was to create jar file form aar file and add it external lib. But somehow it did not worked. 

I am not sure why Google has break down Google Play Services like this. It may be because Android Studio is now official IDE for android development and eclipse is no longer supported. However my problem was still not resolved as I have native eclipse project and in migrating it to Android Studio, I was facing lots of issues. 

Finally I found out solution and that is to switch to revision 28. You can download Google Play Services Revision 28 from Google Repository. Here is the link.

https://dl-ssl.google.com/android/repository/google_play_services_8298000_r28.zip


Download and extract it to separate folder and import it to your eclipse project. That shall solve your issue. 

Hope this post helps you. 

Saturday, October 1, 2016

Android Adding Button Click Action In Notification Bar

Hello,

Recently in one of my project we have a requirement to add button in notification area and do some actions when button is clicked. So here in this blog I am going to explain how to do this.

First of all Add following XML file in your layout and name it as custom_notification. This will have definition for button we are going to add in notification bar.


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="fill_parent"
    android:gravity="center_vertical"
    android:orientation="vertical" >

<Button 
        android:id="@+id/myButton" 
        android:layout_width="wrap_content" 
        android:layout_height="40sp" 
        android:layout_weight="1" 
        android:text="Click Me"
        android:textColor="#000000"
        android:textSize="10sp">
    </Button>


</LinearLayout>

Now in your MainActivity add following function and call it in onCreate method to create notification with button.

public void createNotificationInNotificationBar(){
int icon = R.drawable.ic_launcher;
long when = System.currentTimeMillis();
Notification notification = new Notification(icon, "Click Me To Do Some Action", when);

NotificationManager mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
contentView = new RemoteViews(getPackageName(), R.layout.custom_notification);
notification.contentView = contentView;
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.contentIntent = contentIntent;
notification.flags |= Notification.FLAG_NO_CLEAR; //Do not clear the notification
}

Above code will create notification with button. User will not be able to clear this notification. Now we will add click action handler to button. Add following lines at end of above function.

Intent myIntent = new Intent("button_clicked");
PendingIntent pendingSwitchIntent = PendingIntent.getBroadcast(this, 0, myIntent, 0);
contentView.setOnClickPendingIntent(R.id. myButton, 
                pendingSwitchIntent);

Now add following code to your Manifest.xml file.


This will create receiver and action. Now add receiver to your main activity.

public static class RecordShareReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent myIntent = new Intent(context.getApplicationContext(), ButtonClickActivity.class);
myIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(myIntent);
}
}


This will start ButtonClickActivity. 

public class ButtonClickActivity extends Activity{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//Do your actions here
finish();
}
}

As you can see in above in onCreate method you can do the actions as per your requirement. Once the action is done we finish activity.

Add following code to your android manifest for activity.


android:label="@string/app_name" 
android:launchMode="singleTask" 
android:name=". ButtonClickActivity" 
android:screenOrientation="portrait" 
android:theme="@android:style/Theme.Translucent.NoTitleBar" />


As you can see above we set theme as Translucent theme so activity will not be visible. It will just start do the action and gets finished. 




Saturday, August 13, 2016

Android Record Video in Background Without Preview

Hello,

Recently in one of my project we have a requirement to record video in background with just one button click. No need to display preview or no need of user action, like tap record button. And also stop video recording with just one button click.

In this blog I am going to explain how to do this.

So I have used Android Media Recorder for the same. Since it was not working without preview I added SurfaceView with hidden mode by setting height to 1 so nobody can see it.

First of all lets add some global variables.

View webview;
private Camera mCamera;
private SurfaceView mPreview;
private MediaRecorder mMediaRecorder;
File outputFile;
public static final int MEDIA_TYPE_IMAGE = 1;

public static final int MEDIA_TYPE_VIDEO = 2;

Since we are adding surface view dynamically, add following class to your activity.

    public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
        private SurfaceHolder mHolder;
        private Camera mCamera;

        public CameraPreview(Context context, Camera camera) {
            super(context);
            mCamera = camera;

            // Install a SurfaceHolder.Callback so we get notified when the
            // underlying surface is created and destroyed.
            mHolder = getHolder();
            mHolder.addCallback(this);
            // deprecated setting, but required on Android versions prior to 3.0
            mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        }

        public void surfaceCreated(SurfaceHolder holder) {
            // The Surface has been created, now tell the camera where to draw the preview.
            try {
                mCamera.setPreviewDisplay(holder);
                mCamera.startPreview();
            } catch (IOException e) {
                Log.d(TAG, "Error setting camera preview: " + e.getMessage());
            }
        }

        public void surfaceDestroyed(SurfaceHolder holder) {
            // empty. Take care of releasing the Camera preview in your activity.
        }

        public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
            // If your preview can change or rotate, take care of those events here.
            // Make sure to stop the preview before resizing or reformatting it.

            if (mHolder.getSurface() == null){
              // preview surface does not exist
              return;
            }

            // stop preview before making changes
            try {
                mCamera.stopPreview();
            } catch (Exception e){
              // ignore: tried to stop a non-existent preview
            }

            // set preview size and make any resize, rotate or
            // reformatting changes here

            // start preview with new settings
            try {
                mCamera.setPreviewDisplay(mHolder);
                mCamera.startPreview();

            } catch (Exception e){
                Log.d(TAG, "Error starting camera preview: " + e.getMessage());
            }
        }
    }  

Now first of all lets prepare everything for for recording.

Button startRecording = (Button) findViewById(R.id.startRecording);
startRecording.setOnClickListener( new OnClickListener() {
@Override
public void onClick(View v) {
               initVideoReocrding();
}
});


 public void initVideoReocrding(){
    // Create an instance of Camera
        appView = (Button) findViewById(R.id.mainView)
        mCamera = getCameraInstance();

        // Create our Preview view and set it as the content of our activity.
        mPreview = new CameraPreview(this, mCamera);
        mPreview.setId(107);
        
        android.widget.FrameLayout.LayoutParams params = new android.widget.FrameLayout.LayoutParams(1, 1);
        mPreview.setLayoutParams(params);
        ((ViewGroup) appView.getView().getParent().getParent()).addView(mPreview); 
    }

Since we are adding surface view dynamically we have to call start recording function after sometime.

final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
  @Override
  public void run() {
    startVideoRecording();
  }
}, 1000);

public void startVideoRecording(){
if(prepareVideoRecorder()){
Log.v("MediaRecorder","MediaRecorder is ready");
mMediaRecorder.start();
}
}

Here are the additional function we need for this.

public static Camera getCameraInstance(){
Camera c = null;
try {
c = Camera.open(); // attempt to get a Camera instance
}
catch (Exception e){
// Camera is not available (in use or does not exist)
}
return c; // returns null if camera is unavailable
}

private void releaseMediaRecorder(){
if (mMediaRecorder != null) {
mMediaRecorder.reset();   // clear recorder configuration
mMediaRecorder.release(); // release the recorder object
mMediaRecorder = null;
mCamera.lock();           // lock camera for later use
}
}

private void releaseCamera(){
if (mCamera != null){
mCamera.release();        // release the camera for other applications
mCamera = null;
}
}

/** Create a file Uri for saving an image or video */
private static Uri getOutputMediaFileUri(int type){
 return Uri.fromFile(getOutputMediaFile(type));
}

/** Create a File for saving an image or video */
private static File getOutputMediaFile(int type){
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.

File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
 Environment.DIRECTORY_PICTURES), "MYAPP");
// This location works best if you want the created images to be shared
// between applications and persist after your app has been uninstalled.

// Create the storage directory if it does not exist
if (! mediaStorageDir.exists()){
if (! mediaStorageDir.mkdirs()){
Log.d("MYAPP", "failed to create directory");
return null;
}
}

// Create a media file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File mediaFile;
if (type == MEDIA_TYPE_IMAGE){
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"IMG_"+ timeStamp + ".jpg");
} else if(type == MEDIA_TYPE_VIDEO) {
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"VID_"+ timeStamp + ".mp4");
} else {
return null;
}

return mediaFile;
}

private boolean prepareVideoRecorder(){

Camera temp = getCameraInstance();
if(temp != null){
mCamera = temp;
}
mMediaRecorder = new MediaRecorder();

// Step 1: Unlock and set camera to MediaRecorder
mCamera.unlock();
mMediaRecorder.setCamera(mCamera);

// Step 2: Set sources
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

// Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));

// Step 4: Set output file
outputFile = getOutputMediaFile(MEDIA_TYPE_VIDEO);
mMediaRecorder.setOutputFile(outputFile.toString());

// Step 5: Set the preview output
mMediaRecorder.setPreviewDisplay(mPreview.getHolder().getSurface());
// Step 6: Prepare configured MediaRecorder
try {
mMediaRecorder.prepare();
} catch (IllegalStateException e) {
Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage());
releaseMediaRecorder();
return false;
} catch (IOException e) {
Log.d(TAG, "IOException preparing MediaRecorder: " + e.getMessage());
releaseMediaRecorder();
return false;
}
return true;
}


And following is the function to stop recording.

public void stopVideoRecording(){
    mMediaRecorder.stop();  // stop the recording
        releaseMediaRecorder(); // release the MediaRecorder object
        mCamera.lock();  
        mCamera.stopPreview();
        Log.v("OutputFile", outputFile.getAbsolutePath());
        
        MediaScannerConnection.scanFile(getApplicationContext(), new String[]{outputFile.getAbsolutePath()}, null, null);
        View previewView = ((ViewGroup) appView.getView().getParent().getParent()).findViewById(107);
        ((ViewGroup) appView.getView().getParent().getParent()).removeView(previewView);
    }
    




Cordova Camera Plugin Select Video Returns null FILE URI

Hello,

Recently in one of my project we have a requirement to select video from Gallery and import it to Cordova application.

For that I used following code.

        navigator.camera.getPicture(this.onVideoSuccess, function(){
            console.log('Error in Video Processing);
        }, {
            destinationType: Camera.DestinationType.FILE_URI,
            sourceType: navigator.camera.PictureSourceType.PHOTOLIBRARY,
            mediaType:Camera.MediaType.VIDEO,
            limit: 1
        });

As you can see in above code we are selecting media type to video. Following is our callback function.

    onVideoSuccess: function(videoURI){
        console.log(videoURI);
    }

Now the problem was it was always returns null URI for any video I select. So I decided to dig into plugin code and found issue in CameraLauncher.java. Following is the function.


private void processResultFromGallery(int destType, Intent intent) {
 Uri uri = intent.getData();
        if (uri == null) {
            if (croppedUri != null) {
                uri = croppedUri;
            } else {
                this.failPicture("null data from photo library");
                return;
            }
        }
        .........
        .........
Here we have issue in following line.

        String fileLocation = FileHelper.getRealPath(uri, this.cordova);

This always returns null, however we were getting URI. So I added following code. 

if (this.mediaType != PICTURE) {
         if (fileLocation == null) {
         Cursor cursor = null;
          try
            String[] proj = { MediaStore.Images.Media.DATA };
            cursor = this.webView.getContext().getContentResolver().query(uri,  proj, null, null, null);
            int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            cursor.moveToFirst();
            fileLocation = cursor.getString(column_index);
          } finally {
            if (cursor != null) {
              cursor.close();
            }
          }
         }
            this.callbackContext.success(fileLocation);
        }

and it worked.  Now it returned correct URI.

Hope this helps you.

Cordova / Sencha Touch Application Import and Show Image form Native Phone Gallery

Hello,

Recently in one of my cordova application we have requirement to select images from Phone gallery and display it in list view.  For that we have to allow use to select multiple images from gallery. Here in this blog I am going to explain how to do this.

First of all we will need following plugin installed.

Cordova Image Picture.

https://github.com/wymsee/cordova-imagePicker

If you install this plugin with following command, some how it does not work.

cordova plugin add cordova-plugin-image-picker

So instead of installing it from the command install it with direct git URL.

cordova plugin add "https://github.com/wymsee/cordova-imagePicker"

Along with above plugins, we will also need Cordova File Plugin and Cordova File Transfer Plugin.

Install it with following commands.

cordova plugin add cordova-plugin-file
cordova plugin add cordova-plugin-file-transfer

Now first of all we have to open image gallery with this image picker plugin to choose images.

Add following code.

window.imagePicker.getPictures(
    function(results) {
        for (var i = 0; i < results.length; i++) {
            console.log('Image URI: ' + results[i]);
        }
    }, function (error) {
        console.log('Error: ' + error);
    }
);

Please note that this plugin copy the images it's location temporary location to app directory and returns it's path which is a temporary path so we have to get absolute path to display images. For this modify above code as follow.

window.imagePicker.getPictures(
function(results) {
for (var i = 0; i < results.length; i++) {
console.log(results[i]);

var location= results[i];

window.resolveLocalFileSystemURL(location, function(oFile) {
oFile.file(function(readyFile) {
var reader= new FileReader();
reader.onloadend= function(evt) {
Ext.getStore('ImageGallery').add({
fileURI: evt.target.result
});
Ext.getStore('ImageGallery').sync();
};
reader.readAsDataURL(readyFile);
});
}, function(err){
console.log('### ERR: filesystem.directoryUp() - ' + (JSON.stringify(err)));
});
}

}, function (error) {
console.log('Error: ' + error);
}
);

As you can see in above code, using the temporary location we are getting absolute path of images and adding it to Sencha Touch stores so we use it to display in Sencha Touch data view.

Following is the Store and Model definition.

Ext.define('MyApp.model.ImageGallery',  {
    extend: 'Ext.data.Model',
    config:
    {
        idProperty: 'id',
        fields:
            [

                { name: "id", type: 'int' },
                { name: 'fileURI', type: 'string'}
            ]
    }
});

Ext.define('MyApp.store.ImageGallery', {
    extend: 'Ext.data.Store',
    config:
    {
        autoLoad: true,
        model: 'MyApp.model.ImageGallery',


        proxy:
        {
            type: 'localstorage',
            id: 'image-gallery'
        }
    }
});

And following is the dataview.

 {
xtype: 'dataview',
height: '100%',
width: '100%',
layout: {
type: 'fit'
},
inline: {
wrap: true
},
style: 'background-color:#fefefe',
margin: '6% 6% 6% 6%',
itemTpl: [
 '<div style="width: 140px;height:140px;overflow: hidden">'+
 '<img id="gallery_{id}" style="position: relative;left: 15px;top: -115px;z-index: 1" src="{fileURI}" width="100" height="100"/>'+
 '</div>'

],
store: 'ImageGallery',
itemId: 'imageGalleryDataView',
id: 'imageGalleryDataView',
mode: 'MULTI',
selectedCls: ''
}

Ultimate output will look like this.








Wednesday, June 15, 2016

Android Identify Notification Tap and Go To Particular Fragment In App

Hello,

Recently in one of my project there was a requirement to go to certain section of app as soon as user taps on particular notification in Android top bar. In this blog I am going to mention how to do this.

First of all you have to properly create notification object.

private static void generateNotification(Context context, String message, String type, String date) {

int icon = R.drawable.icon;

long when = System.currentTimeMillis();
NotificationManager notificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification(icon, message, when);
notification.defaults = Notification.DEFAULT_SOUND;
String title = context.getString(R.string.app_name);

Intent notificationIntent = new Intent(context.getApplicationContext(),
MyActivity.class);
notificationIntent.putExtra("msg", message);
notificationIntent.putExtra("type", type);
notificationIntent.putExtra("date", date);

notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent contentIntent = PendingIntent.getActivity(context.getApplicationContext(), 0, notificationIntent, 0);
notification.contentIntent = contentIntent;

// set intent so it does not start a new activity

PendingIntent intent = PendingIntent.getActivity(context, 0,
notificationIntent, 0);
notification.setLatestEventInfo(context, title, message, intent);
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(0, notification);
}

As you can see in above code we have created notificationIntent where I have specified My fragment activity and put all the params we get in notification as extra in intent. No go to your FragmentActivity class and add following code in onCreate method.


onNewIntent(getIntent()); 

And added following code in in your activity

@Override
public void onNewIntent(Intent intent){
    Bundle extras = intent.getExtras();
    if(extras != null){
        if(extras.containsKey("msg"))
        {
            // extract the extra-data in the Notification
            String msg = extras.getString("msg");
            if(isConsiderNewIntent == true){
            Log.v("GCM","GCM message is on new intent"+msg);
            String type = intent.getStringExtra("type");
            String date = intent.getStringExtra("date");
            //check type of message here and go to certain fragment.
            }
        }
    }
}

Hope this helps you.

Saturday, January 2, 2016

Push Google Cloud Message (GCM) from PHP Web Application

In this blog I am going to explain how to push a Google Cloud Message (GCM) from PHP web application.

First of all you will need API key from your Google API account. Go to Google API console and add GCM t your API project and generate a key for Android.  This key you have to add in your PHP script.

Now add following function to your PHP script.

public function send_notification($registatoin_ids, $message) {
     
        // Set POST variables
        $url = 'https://android.googleapis.com/gcm/send';
        $apiKey = 'your_api_key';
     
        $fields = array(
            'registration_ids' => $registatoin_ids,
            'data' => $message,
        );

        $headers = array(
            'Authorization: key=' . $apiKey,
            'Content-Type: application/json'
        );
        // Open connection
        $ch = curl_init();

        // Set the url, number of POST vars, POST data
        curl_setopt($ch, CURLOPT_URL, $url);

        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        // Disabling SSL Certificate support temporarly
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields));

        // Execute post
        $result = curl_exec($ch);
        if ($result === FALSE) {
            die('Curl failed: ' . curl_error($ch));
        }
     
        // Close connection
        curl_close($ch);
        return $result;
    }

As you can see above function takes registration id and message as param and use curl to send push message to particular device. Registration is is unique per device for all the android phones.