Saturday, August 12, 2017

Kotlin - Introduction

Kotlin is a statically-typed programming language ( A programming language is said to use static typing when type checking is performed during compile-time as opposed to run-time) that runs on the Java Virtual Machine and also can be compiled to JavaScript source code or uses the LLVM compiler infrastructure. Its primary development is from a team of JetBrains programmers based in Saint Petersburg, Russia.




Before start learning Kotlin, Let's take a look at Kotlin feature that makes it smart.

Share:

Sunday, April 30, 2017

Android - Moving an object on a circular path


Overview: -  Android provides powerful API libraries which support custom 2D and 3D graphics. We can move an object on the circular path by using canvas and doing some mathematics calculation.



You can use the parametric equation of a circle. Considering the circle is drawn with the center on the origin (O) as shown in the diagram below



If we take a point "p" on the circumference of the circle, having a radius r.

Let the angle made by OP (Origin to p) be θ. Let the distance of p from x-axis be y Let the distance of p from y-axis be x

Using the above assumptions we get the triangle as shown below:

Now we know that cos θ = base/hypotenuse and sin θ = perpendicular/hypotenuse

which gives us cos θ = x/r and sin θ = y/r

If the circle is not at the origin and rather at (a,b) then we can say that the center of the circle is shifted

a unit in the x-axis
b unit in the y-axis 

So for such a circle, we can change the parametric equation accordingly by adding the shift on the x and y-axis giving us the following equations:

x=a+r*cosθ
y=b+r*sinθ 

Where a & b are the x,y coordinates of the center of the circle.

Hence we found x and y the coordinates of the point on the circumference of the circle with radius r

By using the above calculation, We can move an object by using the above method. As we can see:






Now, Let's implement it in Android.

To implement it, we going to use SurfaceView.

import android.content.Context;
import android.graphics.Canvas;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class CircularView extends SurfaceView implements SurfaceHolder.Callback2 {

    private SurfaceHolder mHolder;

    private int mViewWidth;
    private int mViewHeight;
    private Circle mBigCircle;

    private AnimationThread galleryThread = null;

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

        mHolder = getHolder();

        mHolder.addCallback(this);
    }

    @Override
    public void surfaceRedrawNeeded(SurfaceHolder holder) {
        drawViewOnSystemCall(holder);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mViewWidth = getWidth();
        mViewHeight = getHeight();

        mBigCircle = new Circle(mViewWidth / 2, mViewHeight / 2, (Math.round((mViewWidth * 80)          / 100)) / 2);

        drawViewOnSystemCall(holder);

        galleryThread = new AnimationThread(getHolder());
        galleryThread.setRunning(true);
        galleryThread.start();
    }

    private void drawViewOnSystemCall(SurfaceHolder holder) {
        synchronized (holder) {
            refreshScreen();
        }
    }

    private void refreshScreen() {
        Canvas lockCanvas = mHolder.lockCanvas();
        drawSurfaceView(lockCanvas);
        mHolder.unlockCanvasAndPost(lockCanvas);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        drawViewOnSystemCall(holder);
    }

    private void drawSurfaceView(Canvas canvas) {
        if (canvas == null) {
            return;
        }
        canvas.drawRGB(255, 255, 255);
        if (mBigCircle != null) {
            mBigCircle.render(canvas);
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }

    class AnimationThread extends Thread {
        private boolean isRunning;
        private SurfaceHolder mHolder;

        public AnimationThread(SurfaceHolder holder) {
            mHolder = holder;
        }

        public void setRunning(boolean running) {
            isRunning = running;
        }

        @Override
        public void run() {
            while (isRunning) {
                mBigCircle.move();
                drawViewOnSystemCall(mHolder);
            }
        }
    }
}

Create a circle object that shows a circle border and moving object.

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;

public class Circle {

    private float mUserPicCenterX;
    private float mUserPicCenterY;

    private float mUserPicBorderCenterX;
    private float mUserPicBorderCenterY;
    private int mBorderRadius;
    private Paint mVisiblePaint;
    private Paint mVisibleMessageCountPaint;
    private double angle = 230;
    private int mVisibleMessageCountRadius;

    public Circle(float centerX, float centerY, int radius) {
        mUserPicCenterX = centerX;
        mUserPicCenterY = centerY;
        mBorderRadius = radius;
        createVisiblePaint();
        updatePosition(angle);
    }


    private void updatePosition(double angle) {
        angle = Math.toRadians(angle);
        mUserPicBorderCenterX = (float) (mUserPicCenterX + Math.cos(angle) * mBorderRadius);
        mUserPicBorderCenterY = (float) (mUserPicCenterY + Math.sin(angle) * mBorderRadius);
    }

    private void createVisiblePaint() {
        mVisiblePaint = new Paint();
        mVisiblePaint.setAntiAlias(true);
        mVisiblePaint.setFilterBitmap(true);
        mVisiblePaint.setDither(true);
        mVisiblePaint.setColor(Color.parseColor("#F85A74"));
        mVisiblePaint.setStyle(Paint.Style.STROKE);
        mVisiblePaint.setStrokeWidth(14f);

        mVisibleMessageCountPaint = new Paint();
        mVisibleMessageCountPaint.setAntiAlias(true);
        mVisibleMessageCountPaint.setFilterBitmap(true);
        mVisibleMessageCountPaint.setDither(true);
        mVisibleMessageCountPaint.setColor(Color.parseColor("#F85A74"));

        mVisibleMessageCountRadius = mBorderRadius / 6;

    }

    public void render(Canvas canvas) {
        canvas.drawCircle(mUserPicCenterX, mUserPicCenterY, mBorderRadius, mVisiblePaint);
        canvas.drawCircle(mUserPicBorderCenterX, mUserPicBorderCenterY, mVisibleMessageCountRadius, mVisibleMessageCountPaint);
    }


    public void move() {
        if (angle > 360) {
            angle = 0;
        }
        angle++;
        updatePosition(angle);
    }
}

Share:

Saturday, March 18, 2017

Android - App Shortcuts, Android Nougat 7.1 Feature.

Android Nougat 7.1, the newest version of Android has come with several new features. Here I'm going to explain App Shortcuts. App Shortcuts allows the user to move on a specific screen from the app launcher. The feature is available on any launcher that supports them, such as YouTube app launchers, Android Nougat 7.1.


App Shortcuts, YouTube in Android Nougat 7.1


To reveal the shortcuts of an app, simply long-press the launcher icon of that app. Then tap on a shortcut to jump to the associated action. These shortcuts are a great way to engage users and provide some menu options of your app even before users launch your app.

Each shortcut references an intent, each of which launches a specific action or task, and you can create a shortcut for any action that you can express as an intent. For example, you can create intents for sending a new text message, making a reservation, playing a video, continuing a game, loading a map location, and much more.

Implement it we have two ways

 A) Statically  (by declaring all the shortcuts in a resource file, also known as manifest shortcuts).

 B) Dynamically (by adding the shortcuts at runtime).

Let's talk about the static approach.

1. Create a shortcuts.xml file and keep it on following location in the project.

 res/xml/shortcuts.xml
 res/xml-v25/shortcuts.xml
 <shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
            <shortcut
               android:enabled="true"
               android:icon="@drawable/ic_launcher"
               android:shortcutDisabledMessage="@string/shortcut_label_disabled"
               android:shortcutId="new_task"
               android:shortcutLongLabel="@string/shortcut_label_create_new_task"
               android:shortcutShortLabel="@string/shortcut_label_new_task">
                  <intent
                     android:action="android.intent.action.VIEW"
                     android:targetClass="com.test.launchershortcut.DetailActivity"
                     android:targetPackage="com.test.launchershortcut"/>
            </shortcut>

            <shortcut
               android:enabled="true"
               android:icon="@drawable/ic_launcher
               android:shortcutDisabledMessage="@string/shortcut_label_disabled"
               android:shortcutId="opened_tasks"
               android:shortcutLongLabel="@string/shortcut_label_view_opened_tasks"
               android:shortcutShortLabel="@string/shortcut_label_opened_tasks">
                  <intent
                    android:action="android.intent.action.VIEW"
                    android:targetClass="com.test.launchershortcut.ReportActivity
                    android:targetPackage="com.test.launchershortcut"/>
             </shortcut>

  </shortcuts>  

As we can see each shortcut tag defines a series of attributes, like the icon and labels, and also references an intent which is set to launch a specific activity when triggered. Attributes android:shortcutId is a mandatory attribute. If it is not declared it will cause the respective shortcut not to appear in the shortcuts list. The recommended maximum number of shortcuts is 4, although it is possible to publish up to 5.


2. Reference shortcuts.xml file in the AndroidManifest.xml as metadata to the App’s launcher activity.
<activity android:name=".MainActivity">
     <intent-filter>
          <action android:name="android.intent.action.MAIN" />
          <category android:name="android.intent.category.LAUNCHER" />
     </intent-filter>
     <meta-data
           android:name="android.app.shortcuts"
           android:resource="@xml/shortcuts" />
</activity>

Any activity that has the intent-filter action set to android.intent.action.MAIN and the category to android.intent.category.LAUNCHER can display app shortcuts.

Now run the project, the result might look something like this



Implementation of Dynamically Approach. 

For handle Static and Dynamic App shortcuts Nougat 7.1 provides us ShortcutManager. ShortcutManager is the entry point for manipulating (adding, updating, removing) shortcuts at runtime. 
private void createDynamicAppShortCut() {
       ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);
        Intent intent = new Intent(this, DetailActivity.class);
        intent.setAction(Intent.ACTION_VIEW);
        ShortcutInfo shortcut = new ShortcutInfo.Builder(this,                    getString(R.string.shortcut_label_new_task_dy))
                .setShortLabel(getString(R.string.shortcut_label_new_task_dy))
                .setLongLabel(getString(R.string.shortcut_label_new_task_dy))
                .setIcon(Icon.createWithResource(this, R.drawable.ic_launcher))
                .setIntent(intent)
                .build();
        shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut));
}

Trigger the above method at a runtime and move to App Launcher, You will see, we have one more shortcut.



ShortcutManager provides us with some interesting methods.

updateShortcuts (List ids) – update all existing shortcuts by ID.

removeDynamicShortcuts (List ids) – delete dynamic shortcuts by ID.

disableShortcuts (List ids) – disable dynamic shortcuts by ID.

reportShortcutUsed (String shortcutId) – You want to call this method whenever the user selects the shortcut containing the given ID or when the user completes an action in the application that is equivalent to selecting the shortcut. 


Something more

If you tap and drag a shortcut then it will be pinned to the device’s launcher:



When App Shortcuts disable or updated as used, Icon color will be changed. Something like this





Share:

Monday, March 13, 2017

Android - Read Files, Apps, photos & media from Android phone

In this blog, i'm going to write a small code snippet that fetches some useful content from Android Phone.

Below is the snippet


1. Set up Application permissions.


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


2. Get install Application info (APK).      


 In your Activity/Fragment call following method that returns a list of install Application.

      public List<AppInfo> getInstalledApps(Context mContext) {

            List<PackageInfo> apps = mContext.getPackageManager().getInstalledPackages(0);
            ArrayList<AppInfo> mInstalledApps = new ArrayList();

            for (PackageInfo p : apps) {
                if (!isSystemPackage(p)) {
                    AppInfo newInfo = new AppInfo();
                    newInfo.setAppName(p.applicationInfo.loadLabel(mContext.getPackageManager()).toString());
                    newInfo.setPackageName(p.packageName);
                    newInfo.setAapVersion(p.versionName);
                    newInfo.setAppVersionCode(p.versionCode);
                    newInfo.setAppAPKUrl(p.applicationInfo.publicSourceDir);
                    newInfo.setAppIcon(p.applicationInfo.loadIcon(mContext.getPackageManager()));

                    mInstalledApps.add(newInfo);
                }
            }
        }
        return mInstalledApps;
    }

private boolean isSystemPackage(PackageInfo packageInfo) {
        return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
    }

2. Get Video.


In this snippet, We fetch all video in Android Phone.

 public List<MediaModel> getVideos(Context mContext){
   
            ArrayList<MediaModel> mVideos = new ArrayList<>();
            String[] videoProjection = {MediaStore.Video.Media._ID,
                    MediaStore.Video.Media.DATA,
                    MediaStore.Video.Media.DISPLAY_NAME,
                    MediaStore.Video.Media.SIZE,
                    MediaStore.Video.Media.SIZE};

            makeList(mContext.getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, videoProjection, null, null, null), mVideos);
        return mVideos;
    }



3. Get Audio.


In this snippet, We fetch mp3 and wav Audio files from Android Phone.

public List<MediaModel> getAudios(Context mContext) {

            ArrayList<MediaModel> mAudios = new ArrayList<>();

            String[] videoProjection = {MediaStore.Audio.Media._ID,
                    MediaStore.Audio.Media.DATA,
                    MediaStore.Audio.Media.DISPLAY_NAME,
                    MediaStore.Audio.Media.SIZE, MediaStore.Audio.Media.ALBUM_ID};

            makeList(mContext.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, videoProjection,
MediaStore.Files.FileColumns.MIME_TYPE + "=?",
                    new String[]{MimeTypeMap.getSingleton().getMimeTypeFromExtension(mContext.getResources().getString(R.string.wav))}, null), mAudios);

            makeList(mContext.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, videoProjection, MediaStore.Files.FileColumns.MIME_TYPE + "=?",
                    new String[]{MimeTypeMap.getSingleton().getMimeTypeFromExtension(mContext.getResources().getString(R.string.mp3))}, null), mAudios);
   

        return mAudios;
    }

4. Get Image.


In this snippet, We fetch all images from Android Phone.

public List<MediaModel> getImages() {
        if (mImages == null || mImages.isEmpty()) {
            mImages = new ArrayList<>();
            String[] videoProjection = {MediaStore.Images.Media._ID,
                    MediaStore.Images.Media.DATA,
                    MediaStore.Images.Media.DISPLAY_NAME,
                    MediaStore.Images.Media.SIZE,
                    MediaStore.Images.Media.SIZE};

            makeList(mContext.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, videoProjection, null, null, null), mImages);
        }
        return mImages;
    }


5. Text Files.


In this snippet, We fetch only PDF files from Android Phone. You can get all files by changes just extension in below snippet.

public List<MediaModel> getFiles() {

        if (mFiles == null || mFiles.isEmpty()) {
            mFiles = new ArrayList();

            String[] videoProjection = {MediaStore.Files.FileColumns._ID,
                    MediaStore.Files.FileColumns.DATA,
                    MediaStore.Files.FileColumns.DISPLAY_NAME,
                    MediaStore.Files.FileColumns.SIZE};

            makeList(mContext.getContentResolver().query(MediaStore.Files.getContentUri(mContext.getResources().getString(R.string.external)), videoProjection, MediaStore.Files.FileColumns.MIME_TYPE + "=?",
                    new String[]{MimeTypeMap.getSingleton().getMimeTypeFromExtension("pdf")}, null), mFiles);

        return mFiles;
    }


Method and Model classes that are used in the above snippets.


A). makeList(@NonNull Cursor audioFilesCursor, ArrayList<MediaModel> arrayList) 

private void makeList(@NonNull Cursor audioFilesCursor, ArrayList<MediaModel> arrayList) {
        audioFilesCursor.moveToFirst();
        while (!audioFilesCursor.isAfterLast()) {
            MediaModel mediaModel = new MediaModel(audioFilesCursor.getString(1), new File(audioFilesCursor.getString(1)).getName());
            if (mHomeContract.isSelected(audioFilesCursor.getString(1))) {
                mediaModel.setIsSelected(true);
            }
            arrayList.add(mediaModel);
            audioFilesCursor.moveToNext();
        }
        audioFilesCursor.close();
    }

B). MediaModel

public class MediaModel {
    private String url;
    private String name;
    private boolean isSelected;

    /**
     * Instantiates a new Audio model.
     *
     * @param fileUrl  the audio url
     * @param fileName the audio name
     */
    public MediaModel(String fileUrl, String fileName) {
        url = fileUrl;
        name = fileName;
    }

    /**
     * Gets url.
     *
     * @return the url
     */
    public String getUrl() {
        return url;
    }

    /**
     * Gets name.
     *
     * @return the name
     */
    public String getName() {

        return name;
    }

    /**
     * Is selected boolean.
     *
     * @return the boolean
     */
    public boolean isSelected() {
        return isSelected;
    }

    /**
     * Sets is selected.
     *
     * @param isSelected the is selected
     */
    public void setIsSelected(boolean isSelected) {
        this.isSelected = isSelected;
    }
}



C). Create a model that keep information of install Application in your device. 


 public class AppInfo {
              private String appAPKUrl;
              private String appName;
              private String appPackage;
              private String aapVersion;
              private int appVersionCode;
              private Drawable appIcon;
              private boolean isSelected;

              public String getAppAPKUrl() {
                 return appAPKUrl;
              }

             public String getAppName() {
                 return appName;
             }

             public String getPackageName() {
                 return appPackage;
             }

             public String getAapVersion() {
                return aapVersion;
             }

             public int getAppVersionCode() {
                return appVersionCode;
             }

             public Drawable getAppIcon() {
                 return appIcon;
             }


            public boolean isSelected() {
                 return isSelected;
            }

           public void setIsSelected(boolean isSelected) {
               this.isSelected = isSelected;
           }

          public void setAppAPKUrl(String appAPKUrl) {
              this.appAPKUrl = appAPKUrl;
          }

          public void setAppName(String appName) {
               this.appName = appName;
          }

         public void setPackageName(String packageName) {
                this.appPackage = packageName;
         }

        public void setAapVersion(String aapVersion) {
             this.aapVersion = aapVersion;
        }

       public void setAppVersionCode(int appVersionCode) {
         this.appVersionCode = appVersionCode;
       }

       public void setAppIcon(Drawable appIcon) {
         this.appIcon = appIcon;
      }
  }
Share:

Android - Load more and Empty view in RecycleView

If you already read my previous blog that is related to this. Almost same thing i'm going implement here except empty text view. it will visible if list has no items. I'm going to create a custom RecylceView for track load more event.


AppRecyclerView is a customized class of RecyclerView that track load more event on list scroll. 

1. Here is your screen layout file that contains custom recyclerView, progressBar and Textview that shows the empty message "Record not found".


<FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
       >

        <com.demo.AppRecyclerView
            android:id="@+id/list_rv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="none"
            />

        <ProgressBar
            android:id="@+id/list_pb_loader"
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:layout_gravity="bottom|center"
            android:layout_marginBottom="10dp"
            android:background="@color/colorTransparent"
            android:indeterminateTint="@color/colorPrimaryDark"
            android:indeterminateTintMode="src_atop"
            android:visibility="gone"
            />

        <TextView
            android:id="@+id/list_tv_list_msg"
            style="@style/AppBigTextViewStyleLabel"
            android:layout_width="match_parent"
            android:layout_gravity="center"
            android:gravity="center"
            android:text="Record not found"
            android:textColor="@color/colorLightGrey"
            android:visibility="gone"
            />
    </FrameLayout>



2.  In your Activity/Fragment setup the following views.


        ProgressBar progressBar = (ProgressBar) rootView.findViewById(R.id.fragment_product_list_pb_loader);
        progressBar.getIndeterminateDrawable().setColorFilter(ContextCompat.getColor(getContext(), R.color.colorPrimaryDark), PorterDuff.Mode.SRC_IN);

        rvProductList = (AppRecyclerView) rootView.findViewById(R.id.fragment_product_list_rv);

       // Provide load more view that will visible when last item come on screen.
        rvProductList.setLoadMoreProgress(progressBar); 

       //Set listener that invoke when laod more event invoke from custom AppRecyclerView
        rvProductList.setLoadMoreListener(presenter);
           rvProductList.setEmptyView(rootView.findViewById(R.id.fragment_product_list_tv_list_msg));

3. Customize AppRecyclerView


public class AppRecyclerView extends RecyclerView {
    private ProgressBar progressBar;
    private View emptyView;
    private boolean loading;
    private OnLoadMoreListener onLoadMoreListener; 


   //It will observe item of list and show empty view if list not contains any item.
    private AdapterDataObserver emptyObserver = new AdapterDataObserver() {
        @Override
        public void onChanged() {
            Adapter<?> adapter = getAdapter();
            if (adapter != null && emptyView != null) {
                if (adapter.getItemCount() == 0) {
                    emptyView.setVisibility(View.VISIBLE);
                    AppRecyclerView.this.setVisibility(View.GONE);
                } else {
                    emptyView.setVisibility(View.GONE);
                    AppRecyclerView.this.setVisibility(View.VISIBLE);
                }
            }
        }
    };


    public AppRecyclerView(Context context) {
        super(context);
    }


    public AppRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }


    public AppRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setAdapter(Adapter adapter) {
        super.setAdapter(adapter);
        if (adapter != null && !adapter.hasObservers()) {
            adapter.registerAdapterDataObserver(emptyObserver);
        }

        emptyObserver.onChanged();
    }


    public void setEmptyView(View emptyView) {
        this.emptyView = emptyView;
    }

    @Override
    public void stopScroll()
    {
        try {
            super.stopScroll();
        } catch (NullPointerException exception) {
            Crashlytics.logException(exception);
            /**
             *  The mLayout has been disposed of before the
             *  RecyclerView and this stops the application
             *  from crashing.
             */
        }
    }


    public void setLoadMoreListener(OnLoadMoreListener loadMoreListener) {

        this.onLoadMoreListener = loadMoreListener;

        addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView,
                                   int dx, int dy)
            {
                super.onScrolled(recyclerView, dx, dy);
                if (getLayoutManager() instanceof LinearLayoutManager) {
                    validateListScroll(dy, (LinearLayoutManager) getLayoutManager());
                } else {
                    validateGridListScroll(dy, (GridLayoutManager) getLayoutManager());
                }
            }
        });
    }


    private void validateGridListScroll(int dy, GridLayoutManager gridLayoutManager) {
        if (dy > 0 && (gridLayoutManager.getChildCount() + gridLayoutManager.findFirstVisibleItemPosition() + 1) >= gridLayoutManager.getItemCount() && !loading) {
            setLoading(true);
            if (onLoadMoreListener != null) {
                onLoadMoreListener.onLoadMore();
            }
        }
    }


    private void validateListScroll(int dy, LinearLayoutManager linearLayoutManager) {
        if (dy > 0 && (linearLayoutManager.getChildCount() + linearLayoutManager.findFirstVisibleItemPosition() + 1) >= linearLayoutManager.getItemCount() && !loading) {
            setLoading(true);
            if (onLoadMoreListener != null) {
                onLoadMoreListener.onLoadMore();
            }
        }
    }

    public void setLoading(boolean loading) {
        this.loading = loading;
        if (progressBar != null) {
            if (loading) {
                progressBar.setVisibility(View.VISIBLE);
            } else {
                progressBar.setVisibility(View.GONE);
            }
        }
    }


    public void setLoadMoreProgress(ProgressBar progressBar) {
        this.progressBar = progressBar;
    }

    public interface OnLoadMoreListener {
    
        void onLoadMore();
    }
}
Share:

Android - Share multiple files with Wi-Fi Direct

If you already read my previous blog that shows how can we find and connect near available device in Wi-fi Direct environment. In this blog, i'm going to explain how can share multiple files with Wi-Fi Direct.

To achieve this we going to use service.

1. ProgressSenderService is using for send files at the sender side.
2. ProgressReceiverService is using from receive files at the receiver side.


1. Add the services in your project manifest.


   <service android:name=".ProgressReceiverService"/>
   <service android:name=".ProgressSenderService"/>

2. Get address of receiver device.

We can get the address of another device by using the connect() of WifiP2pManager that explains blog steps 5 and 7.


3. Start receiver service.

On file receiver side register and start ProgressReceiverService

Intent receiveIntent = new Intent(activity, ProgressReceiverService.class);
            receiveIntent.setAction(ProgressReceiverService.ACTION_RECEIVE);
            activity.startService(receiveIntent);

            LocalBroadcastManager.getInstance(activity).registerReceiver(mainReceiver, new IntentFilter(ProgressReceiverService.ACTION_RECEIVE));

4. Start sender service.

On file sender side register and start ProgressSenderService with put a bundle that contains full path list of files and receiver address.

Intent receiveIntent = new Intent(activity, ProgressSenderService.class);
                receiveIntent.setAction(ProgressSenderService.ACTION_SEND_FILE);

         
                Bundle b = new Bundle();
                b.putStringArrayList("files", ArrayList<FileFullPathString>);

                receiveIntent.putExtra("files_bundle", b);
                receiveIntent.putExtra("address", p2pInfo.groupOwnerAddress.getHostAddress());
                activity.startService(receiveIntent);

                LocalBroadcastManager.getInstance(activity).registerReceiver(mainReceiver, new IntentFilter(ProgressSenderService.ACTION_SEND_FILE));

5. Sender service.


import android.app.IntentService;
import android.content.Intent;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;

/**
 * A service that process each file transfer request i.e Intent by opening a
 * socket connection with the WiFi Direct Group Owner and writing the file
 */
public class ProgressSenderService extends IntentService {
    private static final int SOCKET_TIMEOUT = 5000;
    public static final String ACTION_SEND_FILE = "com.demo.wifidirect.SEND_FILE";

    public ProgressSenderService(String name) {
        super(name);
    }

    public ProgressSenderService() {
        super("ProgressSenderService");
    }

    /*
     * (non-Javadoc)
     * @see android.app.IntentService#onHandleIntent(android.content.Intent)
     */
    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent.getAction().equals(ACTION_SEND_FILE)) {

            ArrayList<String> files = intent.getBundleExtra("files_bundle").getStringArrayList("files");
            String mAddress = intent.getStringExtra("address");

            Socket socket = new Socket();

            try {          
                socket.bind(null);

                socket.connect(new InetSocketAddress(mAddress, 8988), SOCKET_TIMEOUT);
         
                BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
                DataOutputStream dos = new DataOutputStream(bos);

                dos.writeInt(files.size());

                for (int index = 0; index < files.size(); index++) {
                    ShareFile file = new ShareFile(files.get(index));

                    Log.d("5", file.getPath());
                    long length = file.length();
                    dos.writeLong(length);
                    Log.d("6", "length: " + length);
                    String name = file.getName();

                    dos.writeUTF(name);
                    FileInputStream fis = new FileInputStream(file);
                    BufferedInputStream bis = new BufferedInputStream(fis);

                    transfer(bis, bos, length, index);

                    bis.close();

                    Thread.sleep(1000);
                    Log.d("7", "Client: Data written");
                }

                dos.close();
            } catch (IOException e) {
                Log.e("8", e.getMessage());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                allFileSentNotify();
                if (socket != null) {
                    if (socket.isConnected()) {
                        try {
                            socket.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }

            }
            Log.d("9", "Client: stop service");
        }
    }

    private void transfer(BufferedInputStream bis, BufferedOutputStream bos, long length, int index) throws IOException {
        int theByte;

        int count = 0;
        long totalSent = 0;
        byte[] buffer = new byte[1024];
        while ((theByte = bis.read(buffer)) > 0) {
            try {
                bos.write(buffer, 0, theByte);
            } catch (IOException e) {
                e.printStackTrace();
            }
            bos.flush();

            totalSent += theByte;

            if (count == 50) {
                count = 0;
                     Log.d(TAG, "progress: " + length+"  "+index);
            }
            count++;
        }
    }
}


6. Receiver service.


import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class ProgressReceiverService extends IntentService {
    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */

    private static final String TAG = ProgressReceiverService.class.getName();
    public static final String ACTION_RECEIVE = "action_receive";

    private List<String> mFileName = new ArrayList<>();

    public ProgressReceiverService(String name) {
        super(name);
    }

    private void createDir() {
        new File(android.os.Environment.getExternalStorageDirectory() + "/shareFiles").mkdirs();  
    }

    public ProgressReceiverService() {
        super("ProgressReceiverService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
   
        if (ACTION_RECEIVE.equals(intent.getAction())) {
   
            createDir();


            try {

                /**
                 * Create a server socket and wait for client connections. This
                 * call blocks until a connection is accepted from a client
                 */
                Log.d(TAG, "server create");
                ServerSocket serverSocket = new ServerSocket();
                serverSocket.setReuseAddress(true);
                serverSocket.bind(new InetSocketAddress(8988));
                Socket client = serverSocket.accept();
                Log.d(TAG, "client accept");

                BufferedInputStream bis = new BufferedInputStream(client.getInputStream());
                DataInputStream dis = new DataInputStream(bis);

           
Log.d(TAG, "Receiving "+dis.readInt()+" files");
           

                long totalSent;

                for (int index = 0; index < filesCount; index++) {
                         long fileLength = dis.readLong();
                         long fileSize = fileLength;
                         totalSent = 0;
                          Log.d(TAG, "length: " + fileLength);
                           String fileName = dis.readUTF();
                          Log.d(TAG, "name: " + fileName);

                          FileOutputStream fos = new FileOutputStream(getFile(fileName));
                          int theByte;
                          byte[] buffer = new byte[1024];

                          int count = 0;
                        while (fileLength > 0 && (theByte = dis.read(buffer, 0, (int) Math.min(buffer.length, fileLength))) != -1) {
                                 fos.write(buffer, 0, theByte);
                                 fileLength -= theByte;
                                 totalSent += theByte;

                                 if (count == 50) {
                                      count = 0;
       Log.d(TAG, "progress: " + totalSent+" "+fileSize+" "+index+" "+fileName);
                                }
                             count++;
                    }

                    fos.close();
                    Log.d(TAG, "get file: " + fileName);
                    mFileName.add(fileName);

                    new MediaScannerWrapper(getApplicationContext(), dirPath + "/" + fileName, "image/*").scan();
                }
                dis.close();
                serverSocket.close();
                Log.d(TAG, "saved file and close server");
            } catch (IOException e) {
                Log.e(TAG, e.getMessage() + "");
            }

            stopSelf();
        }
    }

    private File getFile(String fileName) {
        return new File(dirPath + "/" + fileName);
    }

    private class MediaScannerWrapper implements MediaScannerConnection.MediaScannerConnectionClient {
        private MediaScannerConnection mConnection;
        private String mPath;
        private String mMimeType;

        public MediaScannerWrapper(Context context, String filePath, String mime) {
            mPath = filePath;
            mMimeType = mime;
            mConnection = new MediaScannerConnection(context, this);
        }

        public void scan() {
            mConnection.connect();
        }

        @Override
        public void onMediaScannerConnected() {
            mConnection.scanFile(mPath, mMimeType);
        }

        @Override
        public void onScanCompleted(String path, Uri uri) {
            //Empty method
        }
    }
}

Model: ShareFile

import java.io.File;
import java.io.Serializable;

/**
 * class use basic method of File and provide file progress fields
 */
public class ShareFile extends File implements Serializable {
    private String url;

    private int progress;

    private String receivedDate;
    private String fileSize;

    private String customName = "Waiting..";


    public ShareFile(String fileUrl) {
        super(fileUrl);
        url = fileUrl;
    }

    public String getUrl() {
        return url;
    }

    public int getProgress() {
        return progress;
    }

    public String getFileSize() {
        return fileSize;
    }

    public void setProgress(int progress) {
        this.progress = progress;
    }

    public void setFileSize(String fileSize) {
        this.fileSize = fileSize;
    }

    public String getReceivedDate() {
        return receivedDate;
    }

    public void setReceivedData(String receivedDate) {
        this.receivedDate = receivedDate;
    }

    public String getCustomName() {
        return customName;
    }

    public void setCustomName(String customName) {
        this.customName = customName;
    }

}

Share:

Android - At the top speed share files with Wi-Fi Direct

To transfer files at top speed, Android 4.0(API level 14) or later devices with the hardware to connect directly to each other via Wi-Fi without an intermediate access point. Using these APIs, you can discover and connect to other devices when each device supports Wi-Fi Direct, then communicate over a speedy connection across distances much longer than a Bluetooth connection. This is useful for applications that share data among users, such as a multi-player game, Off-line chat or a media sharing application.

Something more about wi-fi direct.

1. For peer-to-peer data transmission, it does not use any kind of traditional home, office or hotspot network.
2. For Security purpose, it uses WPA2 encryption protection.
3. It can transfer data at the speed of 2.5 to 3.0 Mbps.
4. Wi-Fi Direct can operate at up to 100m. Some reference site says 656 feet too.
5. We can also set up group between devices for which hardware support is offered for wifi direct.


Below is the process explained to perform Wi-Fi Direct feature.


1. Set up Application permissions.


In order to use Wi-Fi Direct, add the following permissions to your manifest. Wi-Fi Direct doesn't require an internet connection, but it does use standard Java sockets, which require the INTERNET permission.

   <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

2. Set up a Broadcast Receiver and Peer-to-Peer Manager.


To use Wi-Fi Direct, you need to listen for broadcast intents that tell your application when certain events have occurred. In application, instantiate an IntentFilter and set it to listen for the following:

    @Override
    public void onReceive(Context context, Intent intent)
    {
        String action = intent.getAction();
        if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {          
            if (intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1) == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
                mainPresenterImp.setIsWifiP2pEnabled(true);
            } else {
                mainPresenterImp.setIsWifiP2pEnabled(false);
            }
        } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
                manager.requestPeers(channel, mainPresenterImp);
        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
            if (intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO).isConnected()) {
                manager.requestConnectionInfo(channel, mainPresenterImp);
            } else {
                mainPresenterImp.resetData();
            }
        } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
            mainPresenterImp.updateThisDevice((WifiP2pDevice) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE));
        }
    }

  Now, Create broadcast receiver and register it when the screen is active.

  @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        intentFilter = new IntentFilter();
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);

        manager = (WifiP2pManager) activity.getSystemService(Context.WIFI_P2P_SERVICE);
        channel = manager.initialize(activity, activity.getMainLooper(), null);
    
        receiver = new WiFiDirectBroadcastReceiver(manager, channel, this);
    }

Finally, Add code to register the intent filter and broadcast receiver when your main activity is active, and unregister them.

@Override
    public void onResume() {
        super.onResume();
        registerReceiver(receiver, intentFilter);
    }

3. Initiate Peer Discovery.


To start searching for nearby devices with Wi-Fi Direct, call discoverPeers()  method.

manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {

            @Override
            public void onSuccess()
            {
                Toast.makeText(mActivity, "Discovery Initiated",
                        Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onFailure(int reasonCode)
            {
                Toast.makeText(mActivity, "Discovery Failed : " + reasonCode,
                        Toast.LENGTH_SHORT).show();
            }
        });

4. Fetch the List of Peers.

Now fetch the list of peers. First, implement the WifiP2pManager.PeerListListener interface, which provides information about the peers that Wi-Fi Direct has detected. The following code snippet illustrates this.

private PeerListListener peerListListener = new PeerListListener() {
        @Override
        public void onPeersAvailable(WifiP2pDeviceList peerList) {

            // Out with the old, in with the new.
            peers.clear();
            peers.addAll(peerList.getDeviceList());

            // If an AdapterView is backed by this data, notify it
            // of the change.  For instance, if you have a ListView of available
            // peers, trigger an update.
            ((WiFiPeerListAdapter) getListAdapter()).notifyDataSetChanged();
            if (peers.size() == 0) {
                Log.d(WiFiDirectActivity.TAG, "No devices found");
                return;
            }
        }
    }

5. Connect to a Peer.

In order to connect to a peer, create a new WifiP2pConfig object, and copy data into it from the WifiP2pDevice representing the device you want to connect to. Then call the connect() method.

    @Override
    public void connect() {
        // Picking the first device found on the network.
        WifiP2pDevice device = peers.get(0);

        WifiP2pConfig config = new WifiP2pConfig();
        config.deviceAddress = device.deviceAddress;
        config.wps.setup = WpsInfo.PBC;

        mManager.connect(mChannel, config, new ActionListener() {

            @Override
            public void onSuccess() {
                // WiFiDirectBroadcastReceiver will notify us. Ignore for now.
            }

            @Override
            public void onFailure(int reason) {
                Toast.makeText(WiFiDirectActivity.this, "Connect failed. Retry.",
                        Toast.LENGTH_SHORT).show();
            }
        });
    }

6. Connection Information Available.

WifiP2pManager.ConnectionInfoListener interface. Its onConnectionInfoAvailable() callback will notify you when the state of the connection changes. In cases where multiple devices are going to be connected to a single device (like a game with 3 or more players, or a chat app), one device will be designated the "group owner".

   @Override
    public void onConnectionInfoAvailable(final WifiP2pInfo info) {

        InetAddress groupOwnerAddress = info.groupOwnerAddress.getHostAddress());

        // After the group negotiation, we can determine the group owner.
        if (info.groupFormed && info.isGroupOwner) {
            // Do whatever tasks are specific to the group owner.
            // One common case is creating a server thread and accepting
            // incoming connections.
        } else if (info.groupFormed) {
            // The other device acts as the client. In this case,
            // you'll want to create a client thread that connects to the group
            // owner.
        }
    }

"groupOwnerAddress" provide the address of the device, By using that address we can share files, implement off-line chat Application
So, share a bit at top speed.
Share:

Get it on Google Play

React Native - Start Development with Typescript

React Native is a popular framework for building mobile apps for both Android and iOS. It allows developers to write JavaScript code that ca...