Monday, November 23, 2015

Getting camera captured image in android WebView

Hi all,

Last week was very exciting because I had to get an image from camera and place it inside a WebView. I have googled and couple of results were found as follows.

Here it is said that there is an issue using  "<input type="file" />" in KitKat. At the end of this link there are some links claiming as working modules for lollipop(I did't try them.) Because of this lack of useful codes I decided to share my findings which may help someone. I have tested this code in my nexus 4 running on lollipop. I have added comments in the code to understand the code. Please note that this is only for getting image from camera NOT from file chooser.

 1. AndroidManifest.xml

 <?xml version="1.0" encoding="utf-8"?>  
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
   package="com.duo.wishesapptest" >  
   <uses-permission android:name="android.permission.CAMERA" />  
   <uses-feature android:name="android.hardware.camera" />  
   <uses-permission android:name="android.permission.INTERNET" />  
   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  
   <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />  
   <application  
     android:allowBackup="true"  
     android:icon="@mipmap/ic_launcher"  
     android:label="@string/app_name"  
     android:supportsRtl="true"  
     android:theme="@style/AppTheme" >  
     <activity  
       android:name=".CameraActivity"  
       android:label="@string/title_activity_camera"  
       android:theme="@style/AppTheme.NoActionBar" >  
       <intent-filter>  
         <action android:name="android.intent.action.MAIN" />  
         <category android:name="android.intent.category.LAUNCHER" />  
       </intent-filter>  
     </activity>  
     <activity android:name=".WebContent" >  
     </activity>  
   </application>  
 </manifest>  

2. CameraActivity.java

 package com.duo.wishesapptest;  
 import android.content.Intent;  
 import android.os.Bundle;  
 import android.support.v7.app.AppCompatActivity;  
 import android.support.v7.widget.Toolbar;  
 import android.view.View;  
 public class CameraActivity extends AppCompatActivity {  
   @Override  
   protected void onCreate(Bundle savedInstanceState) {  
     super.onCreate(savedInstanceState);  
     setContentView(R.layout.activity_camera);  
     Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);  
     setSupportActionBar(toolbar);  
   }  
   public void callWebview(View view){  
     Intent intent=new Intent(this,WebContent.class);  
     startActivity(intent);  
   }  
 }  

3. WebContent.java

 package com.duo.wishesapptest;  
 import android.app.Activity;  
 import android.content.Intent;  
 import android.graphics.Bitmap;  
 import android.graphics.BitmapFactory;  
 import android.net.Uri;  
 import android.os.Bundle;  
 import android.os.Environment;  
 import android.provider.MediaStore;  
 import android.support.v7.app.ActionBarActivity;  
 import android.util.Base64;  
 import android.webkit.JavascriptInterface;  
 import android.webkit.WebView;  
 import android.webkit.WebViewClient;  
 import java.io.ByteArrayOutputStream;  
 import java.io.File;  
 public class WebContent extends ActionBarActivity {  
   WebView webView;  
   static Uri picUri = null;  
   String url;  
   String uriInString;  
   boolean isFirstLoad = true; // for the first time there is no image.Used this not to show image in the first page load.  
   @Override  
   protected void onCreate(Bundle savedInstanceState) {  
     super.onCreate(savedInstanceState);  
     setContentView(R.layout.activity_web_content);  
     webView = (WebView) findViewById(R.id.webViewHtm);  
     webView.setWebViewClient(new WebViewClient(){  
       @Override  
       public void onPageFinished(WebView view, String url1) { // This is the important part. Everytime camera intent finish it calls onCreate method.  
         super.onPageFinished(view, url1);          // So after getting the camera image javascript run to set image in WebView  
         if(!isFirstLoad)  
         webView.loadUrl("javascript:getImage('" + url + "')");  
       }  
     });  
     webView.addJavascriptInterface(this, "Android"); // javascript interfaces are in 'this' activity  
                              // if javascript interfaces are added to another class we can't call 'startActivityForResult'  
     webView.loadUrl("https://f073b6f1598031631202dc1cf1408f54a9fede00-www.googledrive.com/host/0B_1iFNsKSU55SkdmbTlwMUQxTWc");  
             // here I published my html file in google drive.this link should work till 2016 may.(google is going to stop this service in 2016)  
             // I believe dropbox is also good to host a simple html page if you don't have a server  
     webView.getSettings().setJavaScriptEnabled(true);  
     webView.getSettings().setDomStorageEnabled(true);  
   }  
   @JavascriptInterface  
   public void showAndroidCamera() { // This is called when click the button in html page.  
     // Create AndroidExampleFolder in storage. You can see this folder by opening Android Device Monitor. See for 'mnt' folder in DDMS --> File Explorer view.  
     File imageStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "AndroidExampleFolder");  
     if (!imageStorageDir.exists()) {  
       imageStorageDir.mkdirs(); // Create AndroidExampleFolder if not exists  
     }  
     // Create camera captured image file path and name  
     File file = new File(imageStorageDir + File.separator + "IMG"+ String.valueOf(System.currentTimeMillis())+ ".jpg");  
     Intent cameraIntent = new Intent("android.media.action.IMAGE_CAPTURE"); // crating the camera intent  
     cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));  
     picUri = Uri.fromFile(file);  
     startActivityForResult(cameraIntent, 123); // result is passed to onActivityResult  
   }  
   @Override  
   protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
     super.onActivityResult(requestCode, resultCode, data);  
     isFirstLoad = false;  
     uriInString = picUri.toString().replace("file://", ""); //needs to remove 'file://' part in uri path.  
     switch (requestCode) {  
       case 123:  
         if (resultCode == Activity.RESULT_OK) {  
           Bitmap bm = BitmapFactory.decodeFile(uriInString);  
           ByteArrayOutputStream baos = new ByteArrayOutputStream();  
           bm.compress(Bitmap.CompressFormat.PNG, 100, baos); //bm is the bitmap object  
           byte[] byteArrayImage = baos.toByteArray();  
           String encodedImage = Base64.encodeToString(byteArrayImage, Base64.DEFAULT);  
           url="data:image/png;base64,"+encodedImage; // encoded value passed to javascript  
         }  
     }  
   }  
 }  

4. activity_camera.xml

 <?xml version="1.0" encoding="utf-8"?>  
 <android.support.design.widget.CoordinatorLayout  
   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"   
   android:fitsSystemWindows="true"  
   tools:context="com.duo.wishesapptest.CameraActivity">  
   <android.support.design.widget.AppBarLayout   
     android:layout_height="wrap_content"  
     android:layout_width="match_parent"   
     android:theme="@style/AppTheme.AppBarOverlay">  
     <android.support.v7.widget.Toolbar   
       android:id="@+id/toolbar"  
       android:layout_width="match_parent"   
       android:layout_height="?attr/actionBarSize"  
       android:background="?attr/colorPrimary"   
       app:popupTheme="@style/AppTheme.PopupOverlay" />  
   </android.support.design.widget.AppBarLayout>  
   <include layout="@layout/content_camera" />  
   <android.support.design.widget.FloatingActionButton   
     android:id="@+id/fab"  
     android:layout_width="wrap_content" android:layout_height="wrap_content"  
     android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin"  
     android:src="@android:drawable/ic_dialog_email" />  
 </android.support.design.widget.CoordinatorLayout>  

5. activity_web_content.xml

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
   xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"  
   android:layout_height="match_parent"  
   tools:context="com.example.lakshan.vers.WebContent">  
   <WebView  
     android:layout_width="match_parent"  
     android:layout_height="300dp"  
     android:id="@+id/webViewHtm"  
     />  
 </RelativeLayout>  

6. content_camera.xml

 <?xml version="1.0" encoding="utf-8"?>  
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
   xmlns:tools="http://schemas.android.com/tools"  
   xmlns:app="http://schemas.android.com/apk/res-auto"  
   android:layout_width="match_parent"  
   android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"  
   android:paddingRight="@dimen/activity_horizontal_margin"  
   android:paddingTop="@dimen/activity_vertical_margin"  
   android:paddingBottom="@dimen/activity_vertical_margin"  
   tools:context="com.duo.wishesapptest.CameraActivity">  
   <Button  
     android:layout_width="wrap_content"  
     android:layout_height="wrap_content"  
     android:text="Get Cam on web view"  
     android:id="@+id/camera_btn"  
     android:layout_alignParentTop="true"  
     android:layout_marginTop="69dp"  
     android:onClick="callWebview"/>  
 </RelativeLayout>  

7. camera.html

 <!DOCTYPE html>  
 <html>  
 <head>  
 <script type="text/javascript">  
   function showAndroidCameraHere() {  
     Android.showAndroidCamera();  
   }       
 function getImage(src) {  
   var img = document.createElement("IMG");  
      img.setAttribute("src", src);  
   img.setAttribute("width", "94");  
   img.setAttribute("height", "70");  
   img.setAttribute("alt", "Your image here");  
   document.getElementById('imageHolder').appendChild(img);  
 }  
 </script>  
 </head>  
 <body>  
 <form id="formContainer"">  
   <h2>capture=camera</h2>  
      <button type="button" onClick="showAndroidCameraHere()">Try it</button>  
      <div id="imageHolder" width="100px" height="80px"></div>  
 </form>  
 </body>  
 </html>  

8. build.gradle

 apply plugin: 'com.android.application'  
 android {  
   compileSdkVersion 22  
   buildToolsVersion "22.0.1"  
   defaultConfig {  
     applicationId "com.duo.wishesapptest"  
     minSdkVersion 19  
     targetSdkVersion 22  
     versionCode 1  
     versionName "1.0"  
   }  
   buildTypes {  
     release {  
       minifyEnabled false  
       proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'  
     }  
   }  
 }  
 dependencies {  
   compile fileTree(dir: 'libs', include: ['*.jar'])  
   testCompile 'junit:junit:4.12'  
   compile 'com.android.support:appcompat-v7:22.2.1'  
   compile 'com.android.support:design:22.2.1'  
 }  

That's it. Hope someone can find this useful. Happy Coding :-).

Friday, September 18, 2015

How to get date from DatePickerDialog to a fragment

Recently I had a need to use a DatePickerDialog. Of course there are many posts on this topic and many stack overflow questions. But the problem was most of them are using Activity to call to a DialogFragment which create a new DatePickerDialog. What i need was a fragment to call a DialogFragment and get the selected date back to the calling fragment. I spend several hours on this topic and found some interesting things which i would like to share. (I'm using the minimum code for simplicity)

First I created a MainActivity which will hold the fragment.

1. MainActivity.java
 public class MainActivity extends Activity {  
   @Override  
   protected void onCreate(Bundle savedInstanceState) {  
     super.onCreate(savedInstanceState);  
     setContentView(R.layout.activity_main);  
   }  
 }  


Here is the layout of MainActivity.(activity_main.xml)

 <RelativeLayout  
   xmlns:android="http://schemas.android.com/apk/res/android"  
   xmlns:tools="http://schemas.android.com/tools"  
   android:layout_width="match_parent"  
   android:layout_height="match_parent"  
   tools:context=".MainActivity">  
   <fragment  
     android:id="@+id/childFragment"  
     android:name="com.primesoft.layouttestingapp.BlankFragment"  
     android:layout_height="fill_parent"  
     android:layout_width="match_parent" />  
 </RelativeLayout>  

Then I created a fragment which will call DatePickerFragment. From now on I used comments to explain the code.

2. BlankFragment.java
 public class BlankFragment extends Fragment implements DatePickerDialog.OnDateSetListener{ // implementing OnDateSetListener  
                                           // This will listen for a date set event  
   ImageButton selectDate;  
   TextView dateToActivate;  
   public BlankFragment() {  
     // Required empty public constructor  
   }  
   @Override  
   public void onCreate(Bundle savedInstanceState) {  
     super.onCreate(savedInstanceState);  
   }  
   @Override  
   public View onCreateView(LayoutInflater inflater, ViewGroup container,  
                Bundle savedInstanceState) {  
     View view;  
     view = inflater.inflate(R.layout.fragment_blank, container, false); // inflating the layout  
     selectDate = (ImageButton)view.findViewById(R.id.SelectDate); // getting the image button in fragment_blank.xml  
     dateToActivate = (TextView) view.findViewById(R.id.selectedDate); // getting the TextView in fragment_blank.xml  
     selectDate.setOnClickListener(new View.OnClickListener() {  // setting listener for user click event  
       @Override  
       public void onClick(View v) {  
         DialogFragment newFragment = new DatePickerFragment(); // creating DialogFragment which creates DatePickerDialog  
         newFragment.setTargetFragment(BlankFragment.this,0);  // Passing this fragment DatePickerFragment.  
         // As i figured out this is the best way to keep the reference to calling activity when using FRAGMENT.  
         newFragment.show(getActivity().getFragmentManager(), "datePicker");  
       }  
     });  
     return view;  
   }  
   @Override  
   public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { // what should be done when a date is selected  
     StringBuilder sb = new StringBuilder().append(dayOfMonth).append("/").append(monthOfYear + 1);  
     String formattedDate = sb.toString();  
     dateToActivate.setText(formattedDate);  
   }  
 }  
Here is the layout for BlankFragment. (fragment_blank.xml)
 <RelativeLayout  
   xmlns:android="http://schemas.android.com/apk/res/android"  
   xmlns:tools="http://schemas.android.com/tools"  
   android:layout_width="match_parent"  
   android:layout_height="match_parent"  
   tools:context="com.primesoft.layouttestingapp.BlankFragment"  
   android:background="@android:color/darker_gray"  
   >  
   <ImageButton  
     android:id="@+id/SelectDate"  
     android:layout_marginTop="10dp"  
     android:layout_marginLeft="10dp"  
     android:layout_width="100dp"  
     android:layout_height="100dp"  
     android:scaleType="fitCenter"  
     android:src="@drawable/date_picker" /> <!--Use your own image here-->     
    <TextView  
     android:id="@+id/selectedDate"  
     android:layout_toRightOf="@id/SelectDate"  
     android:layout_width="120dp"  
     android:layout_height="40dp"  
     android:layout_marginLeft="10dp"  
     android:layout_marginTop="35dp"/>  
 </RelativeLayout>  
And finally here is the DialogFragment which creates DatePickerDialog.

3. DatePickerFragment.java
 public class DatePickerFragment extends DialogFragment{  
   private DatePickerDialog.OnDateSetListener dateSetListener; // listener object to get calling fragment listener  
   DatePickerDialog myDatePicker;  
   @Override  
   public Dialog onCreateDialog(Bundle savedInstanceState) {  
     // Use the current date as the default date in the picker  
     final Calendar c = Calendar.getInstance();  
     int year = c.get(Calendar.YEAR);  
     int month = c.get(Calendar.MONTH);  
     int day = c.get(Calendar.DAY_OF_MONTH);  
     dateSetListener = (DatePickerDialog.OnDateSetListener)getTargetFragment(); // getting passed fragment  
     myDatePicker = new DatePickerDialog(getActivity(), dateSetListener, year, month, day); // DatePickerDialog gets callBack listener as 2nd parameter  
     // Create a new instance of DatePickerDialog and return it  
     return myDatePicker;  
   }  
 }  

Well that's it. I have attached some screen captures here. Hope this helps someone. :-)


Friday, August 28, 2015

The Evaluation of Android API levels

In the search of knowledge in android, I had this worrying question on my mind for a while. That is what are the significant highlights in android API levels and how could they impact to my development. So I search through the internet and found valuable information on android site. But it takes some time to read all these changes so I thought I need to summarize those changes so one can get a bird view on this.(Actually I have listed what I think as important. So I didn’t listed all the changes. Go to this link and you will find all the changes.)


Platform version
API Level
New Additions
Android 1.0
1
Begin of a new era
2
      For T-Mobile G1
3
Cupcake
      SlidingDrawer / HorizontalScrollView
      OnScreen soft keyboard
      Speech recognition via RecognizerIntent
      LocationManager - Applications can get location change updates via Intent
      WebView - Touch start/end/move/cancel DOM event support
      Redesigned Sensor Manager APIs
      GLSurfaceView - convenience framework for creating OpenGL  applications
      Broadcast Intent for app update install succeeded - for smoother app  upgrade experience

4
Donut
      New android.view.animation
      Quick Search Box
      New gesture API
      New android.speech.tts
      New SmsManager
      New DisplayMetrics
      New <supports-screens>
      New <uses-feature> 
      Battery usage indicator
      updated OpenCore 2 media engine
5
Eclair
      ANT support
      New APIs for sync adapters
      Bluetooth Support
      Live Wallpapers
8
Froyo
      android:installLocation install app in external storage
      New android:backupAgent
      generalized backup service on Cloud
      New OpenGL ES 2.0
      Dalvik JIT compiler
      Portable hotspot
      V8 JavaScript engine
      New UI modes "car mode" and "night mode" 
      New ScaleGestureDetector
      The layout attribute fill_parent is renamed to match_parent
      android.permission.SET_TIME — allows an application to set the system time.

9
Gingerbread
      SIP based VOIP android.net.sip (Internet Calling)
      NFC (Near Field Communication)
      gyroscope, rotation vector, linear acceleration, gravity, and barometer
      Multiple cameras support by Camera API 
      New DownloadManager system service
      New  system facility StrictMode
      Support for overscroll
      Text selection controls
      Notification text and icon styles(New TextAppearance.StatusBar.EventContent, TextAppearance.StatusBar.EventContent.Title,TextAppearance.StatusBar.Icon, and TextAppearance.StatusBar.Title)
      supports extra-large screen sizes
      New AlarmClock
      New  Criteria class
      New  StorageManager
10
Gingerbread


      NFC upgrades
11
Honeycomb
      Introducing Fragments
      An application will crash if it does not call through to the super implementation of its Activity.onPause() method.
      Action bar instead of tittle bar
      Support for multi core architecture
      Loader: A framework component that facilitates asynchronous loading of data in combination with UI components to dynamically load data without blocking the main thread. See the Loaders developer guide
      Drag and drop functionality
      HTTP live streaming
      support for Media/Picture Transfer Protocol (MTP/PTP) over USB
      System clipboard
      JsonReader and JsonWriter
      Media sync from SD card
12
Honeycomb
      Exposes an API to its built-in RTP (Real-time Transport Protocol) stack
      homescreen widgets resizeable
      New  LruCache class
      USB APIs
      MTP/PTP APIs

13
Honeycomb
      New resource qualifiers(sw320dp, sw720dp, w720dp, h1024dp)
      tabs are typically presented in the UI using the new ActionBar.newTab()
14
Ice Cream Sandwitch
      Social APIs in contacts provider
      New user profile
      Contact usage feedback
      New calendar API to change data which are stored in Calendar Provider
      New Voicemail Provider
      Camera class now includes APIs for detecting faces and controlling focus and metering areas
      Android Beam
      Wi-Fi peer-to-peer (P2P) connections
      Explore-by-touch mode
      New  VpnService
      New  view group GridLayout
      New  view TextureView
      Navigation bar
      Updated V8 JavaScript compiler
15
Ice Cream Sandwitch
      social stream API in contacts provider
16
Jelly bean
      Isolated services android:isolatedProcess="true" 
      New getMyMemoryState(ActivityManager.RunningAppProcessInfo)
      New intent protocol to directly launch the live wallpaper
      App stack navigation using android:parentActivityName
      The AudioEffect class now supports additional audio pre-processing types when capturing audio
      Android Beam™ now supports large payload transfers over Bluetooth
      The new InputManager class allows you to query the set of input devices
      Extends vsync timing across all drawing and animation done by the Android framework
      Expandable Notification
      Lights out and full screen mode
17
Jelly bean
      Daydream 
      Allows users to add app widgets to the lock screen
      Multiple user spaces on shareable devices such as tablets
      Right-to-left (RTL) UIs and reading direction, such as Arabic and Hebrew
      Nested fragments. Nested fragments are only supported when added to a fragment dynamically
      Renderscript Compute which is the first computation platform ported to run directly on a mobile device GPU
      Wireless display solution that complies with the Miracast
      HDR camera scene mode

18
Jelly bean
      Behavioral changes( load an intent in a different user profile)
      Java interfaces and native support for OpenGL ES 3.0.
      Mipmapping for drawables.
      ViewOverlay class
      Optical background layouts for 9 patch images.
      New rotationAnimation field in WindowManager while screen rotation
      New service class, NotificationListenerService 
      The Trace class
      Android sandbox reinforced with SELinux
      Keystore provider and APIs that allow applications to create exclusive-use keys

19
KitKat
      WebView support for new HTML5 features, and support for remote debugging
      New android.print framework
      The Telephony content provider (the "SMS Provider") allows apps to read and write SMS and MMS messages on the device
      Host card emulation (emulates NFC cards for data exchange)
      New NFC reader mode
      Rate the current track from the remote controller
      New android.transition framework facilitate animations between different states of your user interface
      Built-in step sensors
      System bars partially translucent with new themes, Theme.Holo.NoActionBar.TranslucentDecorand Theme.Holo.Light.NoActionBar.TranslucentDecor.
      In WebView, apps targeting earlier versions will have JS URLs evaluated directly and any result of the evaluation will not replace the current page content. Apps targetting KITKAT or later that load a JS URL will have the result of that URL replace the content of the current page
      New API, ActivityManager.isLowRamDevice(), lets you tune your app's behavior to match the device's memory configuration.
      A new storage access framework
      Full screen immersive mode
      A new tool called procstats helps you analyze the memory resources your app uses
Android 4.4W

      For Android Wear(Android watch)
21
Lollipop
      Material Design
      Screen capturing and screen sharing capabilities with android.media.projection APIs
      Java interfaces and native support for OpenGL ES 3.1.
      New android.hardware.camera2 API to facilitate fine-grain photo capture and image processing
      Act as a Bluetooth LE peripheral device
      improvements in battery life.(project volta)
      new JobScheduler API that lets you optimize battery life by defining jobs for the system to run asynchronously at a later time or under specified conditions (such as when the device is charging)
      New screen pinning API
      Render PDF document pages into bitmap images for printing by using the new PdfRenderer class
      New ART runtime to support a mix of ahead-of-time (AOT), just-in-time (JIT), and interpreted code
      platform support for 64-bit architectures(Nexus 9's NVIDIA Tegra K1)
      New  tilt detector and a heart rate sensor
22
Lollipop
      Multiple SIM card support
Android 6.0
23
M
      App linking
      Automatic full data backup and restore for apps.
      Authenticate users by using fingerprint scans on supported devices
      Support for the Hotspot 2.0 Release 1 spec on Nexus 6 and Nexus 9 devices





So This only a small summary on Android API levels. Hope this may help to someone. :-)