AndroidHive | Tutorials, Games, Apps, Tips http://www.androidhive.info Wed, 08 Oct 2014 15:03:41 +0000 en-US hourly 1 http://wordpress.org/?v=3.9.1 How to Create Google Glass Options Menuhttp://www.androidhive.info/2014/10/how-to-create-google-glass-options-menu/ http://www.androidhive.info/2014/10/how-to-create-google-glass-options-menu/#comments Wed, 08 Oct 2014 15:03:41 +0000 http://www.androidhive.info/?p=25348 My previous tutorial about google glass explains the basic setup required for google glass app development.

In this tutorial I am going to explain how to display menu items on google glass. Displaying options menu on glass is very simple. The menu we are going to create contains three menu items where selecting each menu items performs separate action.

google glass options menu



Let’s starts this by creating a sample glass project.

Creating new Glass Project

1. Create a new project in Eclipse. File ⇒ New ⇒ Android Application Project and give application name, project name and package.

2. Set Minimum Required SDK and Target SDK to API 19: Android 4.4 (KitKat), Compile With to Glass Development Kit Sneak Peek (Google Inc.) (API19) and select the Theme to None

3. Download this drawable folder and paste it in project’s res folder. This folder contains icons required for the menu items.

4. Open strings.xml and add below strings.

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">Glass Menu</string>     
    
    <!-- App start voice command -->
    <string name="start_command">Menus</string>
    
    <!-- View messages -->
    <string name="msg_tap_to_menu">Please tap to lauch "menu"</string>
    <string name="msg_about">Activity for about</string>
    <string name="msg_url">www.androidhive.info</string>
    <string name="msg_settings">Activity for settings</string>
    
    <!-- Menu Items -->
    <string name="action_about">About</string>
    <string name="action_settings">Settings</string>
    <string name="action_quit">Quit</string>
    

</resources>



5. Now create an xml file named main.xml under res ⇒ menu folder. In this file we declare actual menu items. We are going to add three menu items about, settings and quit. The first two menu items will launch two separate activities and the third option will quit the app.

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    
    <item
        android:id="@+id/action_about"
        android:icon="@drawable/ico_about"        
        android:title="@string/action_about"/>
    
    <item
        android:id="@+id/action_settings"
        android:icon="@drawable/ico_settings"        
        android:title="@string/action_settings"/>
    
    <item
        android:id="@+id/action_quit"
        android:icon="@drawable/ico_quit"        
        android:title="@string/action_quit"/>

</menu>



6. Now quickly create two activities for about and settings. These activities will be launched when menu items are selected.

Create activity_about.xml and AboutActivity.java and paste below code.

<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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="@string/msg_about"
        android:textSize="35dp" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:text="@string/msg_url"
        android:textSize="20dp" />

</RelativeLayout>
package info.androidhive.glassmenu;

import android.app.Activity;
import android.os.Bundle;

public class AboutActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_about);
	}
}



Also create activity_settings.xml and SettingsActivity.java and paste below code.

<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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="@string/msg_settings"
        android:textSize="35dp" />

</RelativeLayout>
package info.androidhive.glassmenu;

import android.app.Activity;
import android.os.Bundle;

public class SettingsActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_settings);
	}
}


7. Now open your main activity class (MainActivity.java) and do the below changes. On Glass, menus can be triggered using the same methods which we use when developing mobile app.

Here user tap on D-Pad is detected using KEYCODE_DPAD_CENTER key event inside onKeyDown() method. Then by calling openOptionsMenu() method, menu is rendered on to glass screen. Finally the appropriate action for menu item selection is taken place in onOptionsItemSelected() method.

package info.androidhive.glassmenu;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}

	/**
	 * Opening menu on tapping on D-pad
	 * */
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
			openOptionsMenu();
			return true;
		}
		return false;
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	/**
	 * Taking appropriate action on selecting menu item
	 * */
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		
		switch (item.getItemId()) {
		case R.id.action_about:
			// About menu item selected
			Intent ia = new Intent(MainActivity.this, AboutActivity.class);
			startActivity(ia);
			return true;
		case R.id.action_settings:
			// Settings menu item selected
			Intent is = new Intent(MainActivity.this, SettingsActivity.class);
			startActivity(is);
			return true;
		case R.id.action_quit:
			// Quit menu item selected
			// we'll simply close the app
			finish();
			return true;
		default:
			return super.onOptionsItemSelected(item);
		}
	}

}

Until now we are done with actual menus part. But before testing it, I would like to add the app to ok glass menu to launch the app by voice command.

Adding the app to “Ok Glass” menu

8. Create a new folder named xml under res folder.

9. Inside res ⇒ xml folder create an xml file named voice_trigger_start.xml.

<?xml version="1.0" encoding="utf-8"?>
<trigger keyword="@string/start_command" />

10. Now add DEVELOPMENT permission in the AndroidManifest.xml. This allows us to use unlisted glass voice commands.

To launch the app by voice command, we need to add intent-filter and meta-data by adding appropriate voice packages.

<uses-permission android:name="com.google.android.glass.permission.DEVELOPMENT" />
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidhive.glassmenu"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="19"
        android:targetSdkVersion="19" />
    
    <uses-permission android:name="com.google.android.glass.permission.DEVELOPMENT"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/app_icon"
        android:label="@string/app_name" >
        <activity
            android:name="info.androidhive.glassmenu.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="com.google.android.glass.action.VOICE_TRIGGER" />
            </intent-filter>

            <meta-data
                android:name="com.google.android.glass.VoiceTrigger"
                android:resource="@xml/voice_trigger_start" />
        </activity>
        <activity
            android:name="info.androidhive.glassmenu.AboutActivity"
            android:label="@string/app_name" />
        <activity
            android:name="info.androidhive.glassmenu.SettingsActivity"
            android:label="@string/app_name" />
    </application>

</manifest>

Now deploy the app on to Google Glass and launch the app by saying Ok Glass Menus. Once the app is launched, if you tap on D-Pad you can the menu opened. You can swipe forward / backward to see other menu options.

google glass options menu
]]>
http://www.androidhive.info/2014/10/how-to-create-google-glass-options-menu/feed/ 0
Getting started with Google Glass Developmenthttp://www.androidhive.info/2014/09/getting-started-with-google-glass-development/ http://www.androidhive.info/2014/09/getting-started-with-google-glass-development/#comments Sat, 20 Sep 2014 06:21:18 +0000 http://www.androidhive.info/?p=25115 Today I am very excited to write my first Google Glass tutorial. This is the first article on android wearable devices which is a simple Hello World app. Going forward I’ll cover other android wearables like watches.

I know that Google Glass is very expensive and not everybody afford to have one. But I am sure the price will comedown once the glass is stable and more popular.

As of today there is no official glass emulator to test the apps. But you can try out these options discussed in this forum.

If you are really serious about building apps for glass, you can order it from PlayStore.

google glass hello world program

The following steps will take you through various stages of glass development like downloading GDK, installing drivers, writing a simple hello world program and deploying the same on glass.

1. Downloading GDK (Glass Development Kit)

Glass Development Kit provides APIs to build glassware applications. These APIs won’t be available in all the versions of android.

1 Open SDK manager from your android’s SDK folder.

2 Select Android 4.2.2 (API 19), Google USB Driver (under Extras) and click on Install Packages. This takes a while to download all the packages.

android installing google glass gdk

2. Installing Google Glass Drivers

Android device drivers can be installed either from android SDK’s google usb drivers or with the software that comes from device manufacturer. For Google Glass we can directly use google usb driver. But before installing we need to do a modification in android_winusb.inf, otherwise, your glass device won’t be listed in Eclipse even though you have installed the drivers correctly.

1. Goto your android_SDK_folder\sdk\extras\google\usb_driver and edit android_winusb.inf. Add the below text at the end of the file.

[Google.NTamd64]

;GoogleGlass
%SingleAdbInterface%        = USB_Install, USB\VID_18D1&PID_4E11&REV_0216
%CompositeAdbInterface%     = USB_Install, USB\VID_18D1&PID_4E11&MI_01
%SingleAdbInterface%        = USB_Install, USB\VID_18D1&PID_9001&REV_0216
%CompositeAdbInterface%     = USB_Install, USB\VID_18D1&PID_9001&MI_01

[Google.NTx86]

;GoogleGlass
%SingleAdbInterface%        = USB_Install, USB\VID_18D1&PID_4E11&REV_0216
%CompositeAdbInterface%     = USB_Install, USB\VID_18D1&PID_4E11&MI_01
%SingleAdbInterface%        = USB_Install, USB\VID_18D1&PID_9001&REV_0216
%CompositeAdbInterface%     = USB_Install, USB\VID_18D1&PID_9001&MI_01

2. Now open Run (shortcut Win + R) and type devmgmt.msc and press ok. This opens up Device Manager window.

3. In device manager right click on your Google Glass device and click install drivers. When it ask for browse location, select android_winusb.inf parent folder and follow the instructions.

Following links will help you if you got into any issue.
Google USB Driver
Installing USB Driver
Stackoverflow Discussion


3. Creating new Glass Project

Creating a glass project is same as usual android project, but it differs in choosing proper API version.

1. Create a new project in Eclipse. File ⇒ New ⇒ Android Application Project and give application name, project name and package.

2. Set Minimum Required SDK and Target SDK to API 19: Android 4.4 (KitKat), Compile With to Glass Development Kit Sneak Peek (Google Inc.) (API19) and select the Theme to None

3. Once the project is created open AndroidManifest.xml file and remove the theme android:theme property. By removing this property, glass will apply it’s own theme.

4. Open strings.xml and add below string values.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    
    <string name="app_name">Hello Glass</string>
    <string name="action_settings">Settings</string>
    
    <!-- main activity strings -->
    <string name="hello_glass">Hello Glass !</string>
    <string name="home_url">www.androidhive.info</string>
    
    <!-- "ok glass" voice command -->
    <string name="start_command">Hello Glass</string>    

</resources>



5. Now open your main activity layout file and paste the following code. In my case my activity layout is activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_centerInParent="true"
    android:gravity="center"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_glass" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/home_url"
        android:textSize="20dp" />

</LinearLayout>



6. I haven’t modified anything in MainActivity.java. Your main activity should look like below.

package info.androidhive.helloglass;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}
}

Until now we are done with simple hello world program. In the next step we’ll learn how to deploy the app on the glass device.

4. Running the application on Glass

7. In order to deploy the app on Glass we need to turn on debug option. So on your glass go to Settings ⇒ Device Info ⇒ Turn on debug.

8. Now connect the Glass to your PC using a USB cable. Right click on the project and Run As ⇒ Android Application. Eclipse will list the glass in the list of devices available. Select the glass device and press ok. You should able see the app running on your glass.

google glass hello world program

Now you have successfully developed your first Hello Glass application. But if you close the app or glass goes to sleep, you can’t find the app again anywhere on glass.

In the next step we’ll learn how to add our app to “Ok Glass” menu. So that we can launch our app using a voice command like other glass apps.

5. Adding our app to “Ok Glass” menu

9. Download app_icon.png and paste it in res ⇒ drawable folder. As per glass guidelines, app icon should be 50x50px with white foreground and transparent background.

10. Create a new folder named xml under res folder.

11. Inside res ⇒ xml folder create an xml file named voice_trigger_start.xml.

<?xml version="1.0" encoding="utf-8"?>
<trigger keyword="@string/start_command" />

12. Now add DEVELOPMENT permission in the AndroidManifest.xml which allows us to use unlisted glass voice commands.

Also we need to modify the intent-filter and a meta-data tag for the main activity by adding VOICE_TRIGGER properties.

<uses-permission android:name="com.google.android.glass.permission.DEVELOPMENT" />
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidhive.helloglass"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="15"
        android:targetSdkVersion="15" />

    <uses-permission android:name="com.google.android.glass.permission.DEVELOPMENT" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/app_icon"
        android:label="@string/app_name" >
        <activity
            android:name="info.androidhive.helloglass.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="com.google.android.glass.action.VOICE_TRIGGER" />
            </intent-filter>

            <meta-data
                android:name="com.google.android.glass.VoiceTrigger"
                android:resource="@xml/voice_trigger_start" />
        </activity>
    </application>

</manifest>

Now deploy the app once again on the glass. This time app won’t be launched automatically. You have to launch the app by saying Ok Glass Hello Glass command.

google-glass-ok-glass-menu
]]>
http://www.androidhive.info/2014/09/getting-started-with-google-glass-development/feed/ 0
Housing.com Android App: Hunting properties has never been so easyhttp://www.androidhive.info/2014/09/housing-com-android-app-hunting-properties-has-never-been-so-easy/ http://www.androidhive.info/2014/09/housing-com-android-app-hunting-properties-has-never-been-so-easy/#comments Wed, 10 Sep 2014 12:42:02 +0000 http://www.androidhive.info/?p=23111 For those who have to shift places frequently, the most cumbersome task will be finding a good home, in spite of the fact that there might be thousands of vacant homes, paying guest opportunities and flats in the specific region. More often than not, you get misleading information about properties and spend a lot of money in following such deals. In the long run, however, you end up in complete regression. In addition, it isn’t safe to completely depend upon those brokers, as they might fool you for their profit. For instance, if you are looking for resale flats in Pune, you might see that many options are available, but only a few of them might be ‘real’, particularly in terms of facilities and features. Nevertheless, an Android app from Housing.com — a revolutionary web-based platform to find the dream home for your different purposes, in a matter of a few clicks — is all set to change the way you deal these things! In the article, we will have a fully-fledged review of Housing.com Android application in such a way that you can understand why you should use Housing.com for Android instead of those conventional methods. First, we’ll have an introduction on Housing.com and also at its property search app for Android.

Housing.com Android App


An Introduction to Housing.com & its Android App

If you are an active on social media, you might have heard of Housing.com, which is an emerging platform for online real estate dealings. Conceived and built by a few IIT Alumni, Housing.com did not take that time to grab limelight. In addition to this innovative approach when it comes to the aspect of User Interface and representation of data, major attractive factor of Housing.com is its credibility of data. For instance, when you are checking out listings that you do see in Housing.com, let it be flats, homes, hostels, paying guest opportunities or anything else, you can be sure that even minute details of a listing, such as number of bed rooms or availability of features, will be extremely accurate in every possible manner. Thanks to the developers of this android app, Housing.com for Android does not need you to compromise on quality when compared to its main platform. Thus, despite the fact that you are depending upon one mobile device, you can get the same, unadulterated Housing.com experience. Now, we will have an overview of this application, covering its main functions and other aspects.

As we said earlier, Housing.com for Android is the compressed version of Housing.com, which is meant to work great in Android-powered Smartphones and Tablet PCs. To understand things clearly, we will talk in the ‘practical way’. For instance, suppose you have to attend an interview in Hyderabad and you want to find a good hostel or short stay PG accommodations in that specified area, as you have no other way outs. However, you could not use your PC to find the best hotel in the area. In such situations, you can install Housing.com App in your Android Smartphone or Tablet PC and use its wonderful search feature to find the best option among hundreds of real estate listings. Now, we hope you have a clearer idea on practical instances when Housing.com for Android will be helpful. So, we will move on to noticeable features and other aspects like UI of this application. Even before we begin, we do assure you that Housing.com is the best choice when you are looking for an effective property app.


Finding your Dream Home

Using Housing.com app for finding your dream home is a comparatively easy task. We had used this application, and we were really impressed by its outstanding User Interface that allows users find their dream home without having to face any sort of glitches. In order to find a home or hostel in a specific place, you have to follow some simple steps in Housing.com for Android. First, from the thumbnail-based view, you have to select the city, in which you’d like to find your dream home. Currently, the app supports a number of cities such as Delhi and Hyderabad. Once you have selected the city, you can see a page consisting of a search engine-like UI. In that page, you have to provide desired region of city, either through GPS or selecting from shown list, and perhaps some basic requirements such as number of bedroom. In addition, you have to tell the app, which option you are looking for — rent, buy, agent, or Paying Guest vacancy.

housing searching locality

Then, you have to hit the Search button, upon which you will be shown listing results that match your requirements. Just as it has been shown in Housing.com main website, you can see websites in two forms — in a visual map and as lists. You can use the method according to your convenience, and once you have done, it is all about filtering those results to find the optimum dream home of yours.

housing map view

There are a number of criteria to find the best home. For instance, if you are looking forward to find a paying guest in Gurgaon, you can filter your results in different ways such as rent range, BHK, availability of features like meals, internet, price per person, availability of furnishing, other features, etc. Similarly, when you are on ‘results’ page, you can list those results to according its price range and other aspects. Altogether, the home-finding process of this app is something great, and we had nothing to be disappointed of.


Other Features

• Accuracy & Credibility of Data: What makes Housing.com and its application much popular than others is the fact that Housing.com lists real estate deals only after checking their credibility in every possible way. So, if you see that a Paying Guest opportunity offers internet and meals facility, you can be sure of it, because the team of Housing.com would have confirmed all these aspects before making it live in Housing.com and thus its application.

• Simple UI: As a whole, User Interface of Housing.com is something great, particularly considering its sections for filtering results and posting requirements. Even if you are a newbie to Android, you would find no sort of difficulty in finding your dream home using this app.

housing list property


Our Verdict

While considering all these factors, we find no reasons not to prefer Housing.com for Android to anyone! Especially, we loved its wonderful User Interface and credibility of published data. So, we would prefer this app to everyone, who is looking for the best home out there.

download
]]>
http://www.androidhive.info/2014/09/housing-com-android-app-hunting-properties-has-never-been-so-easy/feed/ 0
Android JSON parsing using Volleyhttp://www.androidhive.info/2014/09/android-json-parsing-using-volley/ http://www.androidhive.info/2014/09/android-json-parsing-using-volley/#comments Sat, 06 Sep 2014 12:53:03 +0000 http://www.androidhive.info/?p=22318 My previous article Android JSON Parsing Tutorial explains parsing json in a simple manner which got very good feedback and good ranking in search engines. In this tutorial I want to explain the same but in a easy & robust way using volley library.

If you want to know more about Volley and it’s benefits, go through Android working with Volley Library.

android json parsing using volley



So let’s start this with a simple project.

Creating New Project

1. Create a new project in Eclipse from File ⇒ New ⇒ Android Application Project and fill all the required details.

I gave my project name as VolleyJson and package name as info.androidhive.volleyjson

2. Now create a new package under src folder by Right clicking on src ⇒ New ⇒ Package and give the package name as app. So my new package name will be info.androidhive.volleyjson.app

3. Download volley.jar and paste it in project’s libs folder.

4. Create a new class named AppController.java under app package. This is going to be a singleton class where we initialize all the volley core objects.

package info.androidhive.volleyjson.app;

import android.app.Application;
import android.text.TextUtils;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;
 
public class AppController extends Application {
 
    public static final String TAG = AppController.class.getSimpleName();
 
    private RequestQueue mRequestQueue;
 
    private static AppController mInstance;
 
    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }
 
    public static synchronized AppController getInstance() {
        return mInstance;
    }
 
    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        }
 
        return mRequestQueue;
    }
 
    public <T> void addToRequestQueue(Request<T> req, String tag) {
        req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
        getRequestQueue().add(req);
    }
 
    public <T> void addToRequestQueue(Request<T> req) {
        req.setTag(TAG);
        getRequestQueue().add(req);
    }
 
    public void cancelPendingRequests(Object tag) {
        if (mRequestQueue != null) {
            mRequestQueue.cancelAll(tag);
        }
    }
}


5. AppController.java has to be executed when the app is launched. So add this class in your AndroidManifest.xml using name attribute for <application> tag.

Also add INTERNET permission as we need to make internet calls from the app.

<application
        android:name="info.androidhive.volleyjson.app.AppController">
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidhive.volleyjson"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="21" />
    
    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" 
        android:name="info.androidhive.volleyjson.app.AppController">
        <activity
            android:name="info.androidhive.volleyjson.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>



6. Open the layout file of main activity and add below code. (In my case my main activity layout file is activity_main.xml).

In this layout we are adding two Buttons and a TextView. In two Button, one is to invoke json object request and other is to invoke json array request. The TextView is used to display the parsed json response.

<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="${relativePackage}.${activityClass}" >

    <Button
        android:id="@+id/btnObjRequest"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="50dp"
        android:paddingLeft="15dp"
        android:paddingRight="15dp"
        android:text="Make JSON Object Request" />

    <Button
        android:id="@+id/btnArrayRequest"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="50dp"
        android:paddingLeft="15dp"
        android:paddingRight="15dp"
        android:text="Make JSON Array Request"
        android:layout_below="@id/btnObjRequest" />
    
    <TextView
        android:id="@+id/txtResponse"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/btnArrayRequest"
        android:layout_marginTop="40px"
        android:padding="20dp" />

</RelativeLayout>


7. Now open main activity class MainActivity.java and add basic code like importing UI elements, adding button click events and initializing other objects.

Below you can notice two methods makeJsonObjectRequest() and makeJsonArrayRequest(). But these methods left empty for now, we’ll add code for these methods in next steps.

package info.androidhive.volleyjson;

import info.androidhive.volleyjson.R;
import info.androidhive.volleyjson.app.AppController;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.android.volley.Request.Method;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.VolleyLog;
import com.android.volley.toolbox.JsonArrayRequest;
import com.android.volley.toolbox.JsonObjectRequest;

public class MainActivity extends Activity {

	// json object response url
	private String urlJsonObj = "http://api.androidhive.info/volley/person_object.json";
	
	// json array response url
	private String urlJsonArry = "http://api.androidhive.info/volley/person_array.json";

	private static String TAG = MainActivity.class.getSimpleName();
	private Button btnMakeObjectRequest, btnMakeArrayRequest;

	// Progress dialog
	private ProgressDialog pDialog;

	private TextView txtResponse;

	// temporary string to show the parsed response
	private String jsonResponse;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		btnMakeObjectRequest = (Button) findViewById(R.id.btnObjRequest);
		btnMakeArrayRequest = (Button) findViewById(R.id.btnArrayRequest);
		txtResponse = (TextView) findViewById(R.id.txtResponse);

		pDialog = new ProgressDialog(this);
		pDialog.setMessage("Please wait...");
		pDialog.setCancelable(false);

		btnMakeObjectRequest.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				// making json object request
				makeJsonObjectRequest();
			}
		});

		btnMakeArrayRequest.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				// making json array request
				makeJsonArrayRequest();
			}
		});

	}

	/**
	 * Method to make json object request where json response starts wtih {
	 * */
	private void makeJsonObjectRequest() {
	}

	/**
	 * Method to make json array request where response starts with [
	 * */
	private void makeJsonArrayRequest() {
	}

	private void showpDialog() {
		if (!pDialog.isShowing())
			pDialog.show();
	}

	private void hidepDialog() {
		if (pDialog.isShowing())
			pDialog.dismiss();
	}
}



Normally json response will be of two types. It can be either json object or json array. If the json starts with {, it is considered to be JSON Object. As well if the json starts with [, then it is JSON Array.

Now we'll see how to make these requests individually.

Making JSON Object Request

8. Volley provides JsonObjectRequest class to make json object request. Add the below code in makeJsonObjectRequest() method. Here we are fetching the json by making a call to below url and parsing it. Finally the parsed response is appended to a string and displayed on the screen.

Sample JSON Object Response

URL: http://api.androidhive.info/volley/person_object.json

{
	"name" : "Ravi Tamada", 
	"email" : "ravi8x@gmail.com",
	"phone" : {
		"home" : "08947 000000",
		"mobile" : "9999999999"
	}
	
}
	/**
	 * Method to make json object request where json response starts wtih {
	 * */
	private void makeJsonObjectRequest() {

		showpDialog();

		JsonObjectRequest jsonObjReq = new JsonObjectRequest(Method.GET,
				urlJsonObj, null, new Response.Listener<JSONObject>() {

					@Override
					public void onResponse(JSONObject response) {
						Log.d(TAG, response.toString());

						try {
							// Parsing json object response
							// response will be a json object
							String name = response.getString("name");
							String email = response.getString("email");
							JSONObject phone = response.getJSONObject("phone");
							String home = phone.getString("home");
							String mobile = phone.getString("mobile");

							jsonResponse = "";
							jsonResponse += "Name: " + name + "\n\n";
							jsonResponse += "Email: " + email + "\n\n";
							jsonResponse += "Home: " + home + "\n\n";
							jsonResponse += "Mobile: " + mobile + "\n\n";

							txtResponse.setText(jsonResponse);

						} catch (JSONException e) {
							e.printStackTrace();
							Toast.makeText(getApplicationContext(),
									"Error: " + e.getMessage(),
									Toast.LENGTH_LONG).show();
						}
						hidepDialog();
					}
				}, new Response.ErrorListener() {

					@Override
					public void onErrorResponse(VolleyError error) {
						VolleyLog.d(TAG, "Error: " + error.getMessage());
						Toast.makeText(getApplicationContext(),
								error.getMessage(), Toast.LENGTH_SHORT).show();
						// hide the progress dialog
						hidepDialog();
					}
				});

		// Adding request to request queue
		AppController.getInstance().addToRequestQueue(jsonObjReq);
	}


Making JSON Array Request

9. Volley provides JsonArrayRequest class to make json array request. Add the below code in makeJsonArrayRequest() method.

Sample JSON Array Response

URL: http://api.androidhive.info/volley/person_array.json

[
	{
	"name" : "Ravi Tamada", 
	"email" : "ravi8x@gmail.com",
	"phone" : {
		"home" : "08947 000000",
		"mobile" : "9999999999"
	}
	},
	{
	"name" : "Tommy", 
	"email" : "tommy@gmail.com",
	"phone" : {
		"home" : "08946 000000",
		"mobile" : "0000000000"
	}
	}
]
	/**
	 * Method to make json array request where response starts with [
	 * */
	private void makeJsonArrayRequest() {

		showpDialog();

		JsonArrayRequest req = new JsonArrayRequest(urlJsonArry,
				new Response.Listener<JSONArray>() {
					@Override
					public void onResponse(JSONArray response) {
						Log.d(TAG, response.toString());

						try {
							// Parsing json array response
							// loop through each json object
							jsonResponse = "";
							for (int i = 0; i < response.length(); i++) {

								JSONObject person = (JSONObject) response
										.get(i);

								String name = person.getString("name");
								String email = person.getString("email");
								JSONObject phone = person
										.getJSONObject("phone");
								String home = phone.getString("home");
								String mobile = phone.getString("mobile");

								jsonResponse += "Name: " + name + "\n\n";
								jsonResponse += "Email: " + email + "\n\n";
								jsonResponse += "Home: " + home + "\n\n";
								jsonResponse += "Mobile: " + mobile + "\n\n\n";

							}

							txtResponse.setText(jsonResponse);

						} catch (JSONException e) {
							e.printStackTrace();
							Toast.makeText(getApplicationContext(),
									"Error: " + e.getMessage(),
									Toast.LENGTH_LONG).show();
						}

						hidepDialog();
					}
				}, new Response.ErrorListener() {
					@Override
					public void onErrorResponse(VolleyError error) {
						VolleyLog.d(TAG, "Error: " + error.getMessage());
						Toast.makeText(getApplicationContext(),
								error.getMessage(), Toast.LENGTH_SHORT).show();
						hidepDialog();
					}
				});

		// Adding request to request queue
		AppController.getInstance().addToRequestQueue(req);
	}

Now run the project and test it once. You should able to see the parsed json displayed on the screen upon tapping the json request buttons.

android json parsing using volley


Complete Code

Here is the complete code of MainActivity.java

package info.androidhive.volleyjson;

import info.androidhive.volleyjson.R;
import info.androidhive.volleyjson.app.AppController;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.android.volley.Request.Method;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.VolleyLog;
import com.android.volley.toolbox.JsonArrayRequest;
import com.android.volley.toolbox.JsonObjectRequest;

public class MainActivity extends Activity {

	// json object response url
	private String urlJsonObj = "http://api.androidhive.info/volley/person_object.json";
	
	// json array response url
	private String urlJsonArry = "http://api.androidhive.info/volley/person_array.json";

	private static String TAG = MainActivity.class.getSimpleName();
	private Button btnMakeObjectRequest, btnMakeArrayRequest;

	// Progress dialog
	private ProgressDialog pDialog;

	private TextView txtResponse;

	// temporary string to show the parsed response
	private String jsonResponse;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		btnMakeObjectRequest = (Button) findViewById(R.id.btnObjRequest);
		btnMakeArrayRequest = (Button) findViewById(R.id.btnArrayRequest);
		txtResponse = (TextView) findViewById(R.id.txtResponse);

		pDialog = new ProgressDialog(this);
		pDialog.setMessage("Please wait...");
		pDialog.setCancelable(false);

		btnMakeObjectRequest.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				// making json object request
				makeJsonObjectRequest();
			}
		});

		btnMakeArrayRequest.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				// making json array request
				makeJsonArrayRequest();
			}
		});

	}

	/**
	 * Method to make json object request where json response starts wtih {
	 * */
	private void makeJsonObjectRequest() {

		showpDialog();

		JsonObjectRequest jsonObjReq = new JsonObjectRequest(Method.GET,
				urlJsonObj, null, new Response.Listener<JSONObject>() {

					@Override
					public void onResponse(JSONObject response) {
						Log.d(TAG, response.toString());

						try {
							// Parsing json object response
							// response will be a json object
							String name = response.getString("name");
							String email = response.getString("email");
							JSONObject phone = response.getJSONObject("phone");
							String home = phone.getString("home");
							String mobile = phone.getString("mobile");

							jsonResponse = "";
							jsonResponse += "Name: " + name + "\n\n";
							jsonResponse += "Email: " + email + "\n\n";
							jsonResponse += "Home: " + home + "\n\n";
							jsonResponse += "Mobile: " + mobile + "\n\n";

							txtResponse.setText(jsonResponse);

						} catch (JSONException e) {
							e.printStackTrace();
							Toast.makeText(getApplicationContext(),
									"Error: " + e.getMessage(),
									Toast.LENGTH_LONG).show();
						}
						hidepDialog();
					}
				}, new Response.ErrorListener() {

					@Override
					public void onErrorResponse(VolleyError error) {
						VolleyLog.d(TAG, "Error: " + error.getMessage());
						Toast.makeText(getApplicationContext(),
								error.getMessage(), Toast.LENGTH_SHORT).show();
						// hide the progress dialog
						hidepDialog();
					}
				});

		// Adding request to request queue
		AppController.getInstance().addToRequestQueue(jsonObjReq);
	}

	/**
	 * Method to make json array request where response starts with [
	 * */
	private void makeJsonArrayRequest() {

		showpDialog();

		JsonArrayRequest req = new JsonArrayRequest(urlJsonArry,
				new Response.Listener<JSONArray>() {
					@Override
					public void onResponse(JSONArray response) {
						Log.d(TAG, response.toString());

						try {
							// Parsing json array response
							// loop through each json object
							jsonResponse = "";
							for (int i = 0; i < response.length(); i++) {

								JSONObject person = (JSONObject) response
										.get(i);

								String name = person.getString("name");
								String email = person.getString("email");
								JSONObject phone = person
										.getJSONObject("phone");
								String home = phone.getString("home");
								String mobile = phone.getString("mobile");

								jsonResponse += "Name: " + name + "\n\n";
								jsonResponse += "Email: " + email + "\n\n";
								jsonResponse += "Home: " + home + "\n\n";
								jsonResponse += "Mobile: " + mobile + "\n\n\n";

							}

							txtResponse.setText(jsonResponse);

						} catch (JSONException e) {
							e.printStackTrace();
							Toast.makeText(getApplicationContext(),
									"Error: " + e.getMessage(),
									Toast.LENGTH_LONG).show();
						}

						hidepDialog();
					}
				}, new Response.ErrorListener() {
					@Override
					public void onErrorResponse(VolleyError error) {
						VolleyLog.d(TAG, "Error: " + error.getMessage());
						Toast.makeText(getApplicationContext(),
								error.getMessage(), Toast.LENGTH_SHORT).show();
						hidepDialog();
					}
				});

		// Adding request to request queue
		AppController.getInstance().addToRequestQueue(req);
	}

	private void showpDialog() {
		if (!pDialog.isShowing())
			pDialog.show();
	}

	private void hidepDialog() {
		if (pDialog.isShowing())
			pDialog.dismiss();
	}
}
]]>
http://www.androidhive.info/2014/09/android-json-parsing-using-volley/feed/ 0
Android Building Free Wallpapers App – Part 2http://www.androidhive.info/2014/08/android-building-free-wallpapers-app-part-2/ http://www.androidhive.info/2014/08/android-building-free-wallpapers-app-part-2/#comments Wed, 20 Aug 2014 06:18:05 +0000 http://www.androidhive.info/?p=20755 In the 1st Part we prepared required classes for this app. In this we’ll start adding one by one screen to the app and at the end of the article we should be able to complete the application.

android-free-wallpapers-app-banner-2


Styling The Action Bar

12. You might noticed that this app action bar is having customized background color. That can be done by defining a custom theme in res ⇒ values ⇒ styles.xml folder. Also this is not a complete action bar customization. I am just changing the background color of it.

If you want to customize it completely, Styling the Action Bar will helps you. Add below code in res ⇒ values ⇒ styles.xml. If you don’t find styles.xml, create a new file in values folder.

<resources>

    <style name="FreeWallTheme" parent="@android:style/Theme.Holo.Light">
        <item name="android:actionBarStyle">@style/MyActionBarTheme</item>
        <item name="android:actionOverflowButtonStyle">@style/OverFlow</item>
    </style>

    <style name="MyActionBarTheme" parent="@android:style/Widget.Holo.Light.ActionBar">
        <item name="android:background">@color/action_bar</item>
        <item name="android:titleTextStyle">@style/TitleTextStyle</item>
    </style>

    <style name="TitleTextStyle" parent="android:TextAppearance.Holo.Widget.ActionBar.Title">
        <item name="android:textColor">@color/action_bar_title</item>
    </style>

    <style name="OverFlow" parent="android:style/Widget.Holo.ActionButton.Overflow">
        <item name="android:src">@drawable/ic_action_overflow</item>
    </style>

</resources>



13. Now in order to apply this style, open the AndroidManifest.xml file and add the style using android:theme attribute for tag. Also add below permissions too.

INTERNET – To consume internet
WRITE_EXTERNAL_STORAGE – To store wallpapers in SDCard
SET_WALLPAPER – To update the device wallpaper

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidhive.awesomewallpapers"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="19" />

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

    <application
        android:name="info.androidhive.awesomewallpapers.app.AppController"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/FreeWallTheme" >
        <!-- all the activities goes here -->
    </application>

</manifest>

Now if you run the app, you should see the action bar color changed.

Adding the Splash Screen

Splash screens are not necessary for every app. But for our app it make sense to have a splash screen as we have to download wallpapers categories before going into the app. In brief we make a request to Picasa to get list of public albums (in our case those are wallpapers categories) and store them in shared preferences. So let’s create the necessary files for the splash screen.

14. Create an xml file named activity_splash.xml under res ⇒ layout folder.

<?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="match_parent"
    android:orientation="vertical" >

    <ImageView android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:src="@drawable/splash_screen"
        android:scaleType="centerCrop"/>
    

</LinearLayout>
android freewallpapers app splash screen



15. Now create a class named SplashActivity.java in the app main package and paste the below code. Here we are making a json call to Picasa and fetching list of albums. Once albums are fetched we are storing them in Shared Preferences. Later these categories will be loaded into navigation drawer.

package info.androidhive.awesomewallpapers;

import info.androidhive.awesomewallpapers.app.AppConst;
import info.androidhive.awesomewallpapers.app.AppController;
import info.androidhive.awesomewallpapers.picasa.model.Category;

import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Window;
import android.widget.Toast;

import com.android.volley.Request.Method;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;

public class SplashActivity extends Activity {
	private static final String TAG = SplashActivity.class.getSimpleName();
	private static final String TAG_FEED = "feed", TAG_ENTRY = "entry",
			TAG_GPHOTO_ID = "gphoto$id", TAG_T = "$t",
			TAG_ALBUM_TITLE = "title";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
		getActionBar().hide();
		setContentView(R.layout.activity_splash);

		// Picasa request to get list of albums
		String url = AppConst.URL_PICASA_ALBUMS
				.replace("_PICASA_USER_", AppController.getInstance()
						.getPrefManger().getGoogleUserName());
		
		Log.d(TAG, "Albums request url: " + url);

		// Preparing volley's json object request
		JsonObjectRequest jsonObjReq = new JsonObjectRequest(Method.GET, url,
				null, new Response.Listener<JSONObject>() {

					@Override
					public void onResponse(JSONObject response) {
						Log.d(TAG, "Albums Response: " + response.toString());
						List<Category> albums = new ArrayList<Category>();
						try {
							// Parsing the json response
							JSONArray entry = response.getJSONObject(TAG_FEED)
									.getJSONArray(TAG_ENTRY);

							// loop through albums nodes and add them to album
							// list
							for (int i = 0; i < entry.length(); i++) {
								JSONObject albumObj = (JSONObject) entry.get(i);
								// album id
								String albumId = albumObj.getJSONObject(
										TAG_GPHOTO_ID).getString(TAG_T);

								// album title
								String albumTitle = albumObj.getJSONObject(
										TAG_ALBUM_TITLE).getString(TAG_T);

								Category album = new Category();
								album.setId(albumId);
								album.setTitle(albumTitle);

								// add album to list
								albums.add(album);

								Log.d(TAG, "Album Id: " + albumId
										+ ", Album Title: " + albumTitle);
							}

							// Store albums in shared pref
							AppController.getInstance().getPrefManger()
									.storeCategories(albums);

							// String the main activity
							Intent intent = new Intent(getApplicationContext(),
									MainActivity.class);
							startActivity(intent);
							// closing spalsh activity
							finish();

						} catch (JSONException e) {
							e.printStackTrace();
							Toast.makeText(getApplicationContext(),
									getString(R.string.msg_unknown_error),
									Toast.LENGTH_LONG).show();
						}

					}
				}, new Response.ErrorListener() {

					@Override
					public void onErrorResponse(VolleyError error) {
						Log.e(TAG, "Volley Error: " + error.getMessage());

						// show error toast
						Toast.makeText(getApplicationContext(),
								getString(R.string.splash_error),
								Toast.LENGTH_LONG).show();

						// Unable to fetch albums
						// check for existing Albums data in Shared Preferences
						if (AppController.getInstance().getPrefManger()
								.getCategories() != null && AppController.getInstance().getPrefManger()
								.getCategories().size() > 0) {
							// String the main activity
							Intent intent = new Intent(getApplicationContext(),
									MainActivity.class);
							startActivity(intent);
							// closing spalsh activity
							finish();
						} else {
							// Albums data not present in the shared preferences
							// Launch settings activity, so that user can modify
							// the settings

							Intent i = new Intent(SplashActivity.this,
									SettingsActivity.class);
							// clear all the activities
							i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
									| Intent.FLAG_ACTIVITY_CLEAR_TASK);
							startActivity(i);
						}

					}
				});

		// disable the cache for this request, so that it always fetches updated
		// json
		jsonObjReq.setShouldCache(false);

		// Making the request
		AppController.getInstance().addToRequestQueue(jsonObjReq);

	}

}

16. Now add the SplashActivity as launcher activity in the AndroidManifest.xml inside <application>

   <application ....>
        <activity
            android:name="info.androidhive.awesomewallpapers.SplashActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

Now if you run the app, you should see albums json logged in LogCat. If yes, we can move to the next step i.e adding navigation drawer and displaying the wallpaper categories.

Adding Navigation Drawer

Before following this step make sure that you read Android Sliding Menu using Navigation Drawer tutorial to make this step simple for you. To add navigation drawer, we need to create fewer layouts and classes.

17. Create drawer_list_item.xml under res ⇒ layout directory. This is the layout file for navigation drawer list item to display wallpaper category name.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="48dp" 
    android:background="@drawable/list_selector">

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"        
        android:minHeight="?android:attr/listPreferredItemHeightSmall"
        android:textAppearance="?android:attr/textAppearanceListItemSmall"
        android:textColor="@color/list_item_title"
        android:gravity="center_vertical"
        android:paddingRight="10dp"
        android:paddingLeft="10dp"/>

</RelativeLayout>



18. Create NavDrawerItem.java in your project’s main package and paste the below code. This is a model class for navigation list item.



19. Now we need to create the custom list adapter class which provides data to navigation list view. Create a class named NavDrawerListAdapter.java under adapter package. This adapter class inflates the drawer_list_item.xml layout by displaying appropriate wallpaper category name.



20. Now we need to create a list view element in our main activity. Create an xml named activity_main.xml with the below content.

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- Framelayout to display Fragments -->
    <FrameLayout
        android:id="@+id/frame_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <!-- Listview to display slider menu -->
    <ListView
        android:id="@+id/list_slidermenu"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:choiceMode="singleChoice"
        android:divider="@color/list_divider"
        android:dividerHeight="1dp"        
        android:listSelector="@drawable/list_selector"
        android:background="@color/list_background"/>
</android.support.v4.widget.DrawerLayout>



21. Open your main activity class (in my case MainActivity.java) and add the below code. Eclipse might show you errors on the lines where you see GridFragment. For now comment those lines.

package info.androidhive.awesomewallpapers;

import info.androidhive.awesomewallpapers.app.AppController;
import info.androidhive.awesomewallpapers.helper.NavDrawerListAdapter;
import info.androidhive.awesomewallpapers.picasa.model.Category;

import java.util.ArrayList;
import java.util.List;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.widget.DrawerLayout;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;

public class MainActivity extends Activity {
	private static final String TAG = MainActivity.class.getSimpleName();
	private DrawerLayout mDrawerLayout;
	private ListView mDrawerList;
	private ActionBarDrawerToggle mDrawerToggle;

	// Navigation drawer title
	private CharSequence mDrawerTitle;
	private CharSequence mTitle;
	private List<Category> albumsList;
	private ArrayList<NavDrawerItem> navDrawerItems;
	private NavDrawerListAdapter adapter;

	@SuppressLint("NewApi")
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		mTitle = mDrawerTitle = getTitle();

		mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
		mDrawerList = (ListView) findViewById(R.id.list_slidermenu);

		navDrawerItems = new ArrayList<NavDrawerItem>();

		// Getting the albums from shared preferences
		albumsList = AppController.getInstance().getPrefManger().getCategories();

		// Insert "Recently Added" in navigation drawer first position
		Category recentAlbum = new Category(null,
				getString(R.string.nav_drawer_recently_added));

		albumsList.add(0, recentAlbum);

		// Loop through albums in add them to navigation drawer adapter
		for (Category a : albumsList) {
			navDrawerItems.add(new NavDrawerItem(a.getId(), a.getTitle()));
		}

		mDrawerList.setOnItemClickListener(new SlideMenuClickListener());

		// Setting the nav drawer list adapter
		adapter = new NavDrawerListAdapter(getApplicationContext(),
				navDrawerItems);
		mDrawerList.setAdapter(adapter);

		// Enabling action bar app icon and behaving it as toggle button
		getActionBar().setDisplayHomeAsUpEnabled(true);
		getActionBar().setHomeButtonEnabled(true);
		getActionBar().setIcon(
				new ColorDrawable(getResources().getColor(
						android.R.color.transparent)));

		mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
				R.drawable.ic_drawer, R.string.app_name, R.string.app_name) {
			public void onDrawerClosed(View view) {
				getActionBar().setTitle(mTitle);
				// calling onPrepareOptionsMenu() to show action bar icons
				invalidateOptionsMenu();
			}

			public void onDrawerOpened(View drawerView) {
				getActionBar().setTitle(mDrawerTitle);
				// calling onPrepareOptionsMenu() to hide action bar icons
				invalidateOptionsMenu();
			}
		};
		mDrawerLayout.setDrawerListener(mDrawerToggle);

		if (savedInstanceState == null) {
			// on first time display view for first nav item
			displayView(0);
		}
	}

	/**
	 * Navigation drawer menu item click listener
	 * */
	private class SlideMenuClickListener implements
			ListView.OnItemClickListener {
		@Override
		public void onItemClick(AdapterView<?> parent, View view, int position,
				long id) {
			// display view for selected nav drawer item
			displayView(position);
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	/**
	 * On menu item selected
	 * */
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// toggle nav drawer on selecting action bar app icon/title
		if (mDrawerToggle.onOptionsItemSelected(item)) {
			return true;
		}
		// Handle action bar actions click
		switch (item.getItemId()) {
		case R.id.action_settings:
			// Selected settings menu item
			// launch Settings activity
			Intent intent = new Intent(MainActivity.this,
					SettingsActivity.class);
			startActivity(intent);
			return true;
		default:
			return super.onOptionsItemSelected(item);
		}
	}

	/**
	 * Called when invalidateOptionsMenu() is triggered
	 */
	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		// if nav drawer is opened, hide the action items
		boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
		menu.findItem(R.id.action_settings).setVisible(!drawerOpen);
		return super.onPrepareOptionsMenu(menu);
	}

	/**
	 * Diplaying fragment view for selected nav drawer list item
	 * */
	private void displayView(int position) {
		// update the main content by replacing fragments
		Fragment fragment = null;
		switch (position) {
		case 0:
			// Recently added item selected
			// don't pass album id to grid fragment
			fragment = GridFragment.newInstance(null);
			break;

		default:
			// selected wallpaper category
			// send album id to grid fragment to list all the wallpapers
			String albumId = albumsList.get(position).getId();
			fragment = GridFragment.newInstance(albumId);
			break;
		}

		if (fragment != null) {
			FragmentManager fragmentManager = getFragmentManager();
			fragmentManager.beginTransaction()
					.replace(R.id.frame_container, fragment).commit();

			// update selected item and title, then close the drawer
			mDrawerList.setItemChecked(position, true);
			mDrawerList.setSelection(position);
			setTitle(albumsList.get(position).getTitle());
			mDrawerLayout.closeDrawer(mDrawerList);
		} else {
			// error in creating fragment
			Log.e(TAG, "Error in creating fragment");
		}
	}

	@Override
	public void setTitle(CharSequence title) {
		mTitle = title;
		getActionBar().setTitle(mTitle);
	}

	/**
	 * When using the ActionBarDrawerToggle, you must call it during
	 * onPostCreate() and onConfigurationChanged()...
	 */

	@Override
	protected void onPostCreate(Bundle savedInstanceState) {
		super.onPostCreate(savedInstanceState);
		// Sync the toggle state after onRestoreInstanceState has occurred.
		mDrawerToggle.syncState();
	}

	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		super.onConfigurationChanged(newConfig);
		// Pass any configuration change to the drawer toggls
		mDrawerToggle.onConfigurationChanged(newConfig);
	}
}

22. Add the MainActivity.java activity in the AndroidManifest.xml inside <application> tag.

<application ...>
        <activity
            android:name="info.androidhive.awesomewallpapers.MainActivity"
            android:screenOrientation="portrait" >
        </activity>
</application>

Now run the app and test it once. You should see the wallpaper categories displayed in navigation drawer.

android free wallpapers app navigation drawer


Wallpapers Grid Thumbnail Preview

The next step is displaying the wallpapers of selected category in a thumbnail fashion. For this we need a GridView and few other class files.

23. Create an xml file named grid_item_photo.xml under res ⇒ layout and add the below code. This layout defines single grid item tile in the GridView. Here we are using volley’s NetworkImageView to download the image from an URL and display.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@color/grid_item_bg" >

    <ImageView
        android:id="@+id/imgLoader"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_centerInParent="true"
        android:src="@drawable/ico_loader" />

    <!-- Thumbnail Image -->

    <com.android.volley.toolbox.NetworkImageView
        android:id="@+id/thumbnail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</RelativeLayout>



24. Create another layout file named fragment_grid.xml under res ⇒ layout folder. This layout contains actual GridView and a ProgressBar to show to loading progress while the request is in process.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" 
    android:background="@color/black">

    <ProgressBar
        android:id="@+id/pbLoader"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true" 
        style="?android:attr/progressBarStyle">
    </ProgressBar>

    <GridView
        android:id="@+id/grid_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="@color/grid_bg"
        android:gravity="center"
        android:numColumns="auto_fit"
        android:stretchMode="columnWidth">
    </GridView>

</RelativeLayout>



25. Now just like navigation drawer adapter, we need to create another adapter class to provide data to GridView. So create a class named GridViewAdapter.java under adapter package. This class inflates grid_item_photo.xml layout with appropriate wallpaper image.

package info.androidhive.awesomewallpapers.helper;

import info.androidhive.awesomewallpapers.R;
import info.androidhive.awesomewallpapers.app.AppController;
import info.androidhive.awesomewallpapers.picasa.model.Wallpaper;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.RelativeLayout;

import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.NetworkImageView;

public class GridViewAdapter extends BaseAdapter {

	private Activity _activity;
	private LayoutInflater inflater;
	private List<Wallpaper> wallpapersList = new ArrayList<Wallpaper>();
	private int imageWidth;
	ImageLoader imageLoader = AppController.getInstance().getImageLoader();

	public GridViewAdapter(Activity activity, List<Wallpaper> wallpapersList,
			int imageWidth) {
		this._activity = activity;
		this.wallpapersList = wallpapersList;
		this.imageWidth = imageWidth;
	}

	@Override
	public int getCount() {
		return this.wallpapersList.size();
	}

	@Override
	public Object getItem(int position) {
		return this.wallpapersList.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		if (inflater == null)
			inflater = (LayoutInflater) _activity
					.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		if (convertView == null)
			convertView = inflater.inflate(R.layout.grid_item_photo, null);

		if (imageLoader == null)
			imageLoader = AppController.getInstance().getImageLoader();

		// Grid thumbnail image view
		NetworkImageView thumbNail = (NetworkImageView) convertView
				.findViewById(R.id.thumbnail);

		Wallpaper p = wallpapersList.get(position);

		thumbNail.setScaleType(ImageView.ScaleType.CENTER_CROP);
		thumbNail.setLayoutParams(new RelativeLayout.LayoutParams(imageWidth,
				imageWidth));
		thumbNail.setImageUrl(p.getUrl(), imageLoader);

		return convertView;
	}

}



26. Create a class named GridFragment.java under the project’s main package. This is where we implement actual GridView implementation. This is main fragment class will be loaded when an item from navigation drawer selected. Briefly it works as below

> User selects a wallpaper category from navigation drawer.
> The selected category id (in other words album id) will be passed to GridFragment
> Using the category id, we make another request to Picasa to get all the photos under that category.
> Finally the photos json will be parsed and wallpaper model objects will be passed to GridViewAdpater to render the wallpapers in grid view.

package info.androidhive.awesomewallpapers;

import info.androidhive.awesomewallpapers.app.AppConst;
import info.androidhive.awesomewallpapers.app.AppController;
import info.androidhive.awesomewallpapers.helper.GridViewAdapter;
import info.androidhive.awesomewallpapers.picasa.model.Wallpaper;
import info.androidhive.awesomewallpapers.util.PrefManager;
import info.androidhive.awesomewallpapers.util.Utils;

import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Fragment;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.GridView;
import android.widget.ProgressBar;
import android.widget.Toast;

import com.android.volley.Request.Method;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;

public class GridFragment extends Fragment {
	private static final String TAG = GridFragment.class.getSimpleName();
	private Utils utils;
	private GridViewAdapter adapter;
	private GridView gridView;
	private int columnWidth;
	private static final String bundleAlbumId = "albumId";
	private String selectedAlbumId;
	private List<Wallpaper> photosList;
	private ProgressBar pbLoader;
	private PrefManager pref;

	// Picasa JSON response node keys
	private static final String TAG_FEED = "feed", TAG_ENTRY = "entry",
			TAG_MEDIA_GROUP = "media$group",
			TAG_MEDIA_CONTENT = "media$content", TAG_IMG_URL = "url",
			TAG_IMG_WIDTH = "width", TAG_IMG_HEIGHT = "height", TAG_ID = "id",
			TAG_T = "$t";

	public GridFragment() {
	}

	public static GridFragment newInstance(String albumId) {
		GridFragment f = new GridFragment();
		Bundle args = new Bundle();
		args.putString(bundleAlbumId, albumId);
		f.setArguments(args);
		return f;
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {

		photosList = new ArrayList<Wallpaper>();
		pref = new PrefManager(getActivity());

		// Getting Album Id of the item selected in navigation drawer
		// if Album Id is null, user is selected recently added option
		if (getArguments().getString(bundleAlbumId) != null) {
			selectedAlbumId = getArguments().getString(bundleAlbumId);
			Log.d(TAG,
					"Selected album id: "
							+ getArguments().getString(bundleAlbumId));
		} else {
			Log.d(TAG, "Selected recentlyed added album");
			selectedAlbumId = null;
		}

		// Preparing the request url
		String url = null;
		if (selectedAlbumId == null) {
			// Recently added album url
			url = AppConst.URL_RECENTLY_ADDED.replace("_PICASA_USER_",
					pref.getGoogleUserName());
		} else {
			// Selected an album, replace the Album Id in the url
			url = AppConst.URL_ALBUM_PHOTOS.replace("_PICASA_USER_",
					pref.getGoogleUserName()).replace("_ALBUM_ID_",
					selectedAlbumId);
		}

		Log.d(TAG, "Final request url: " + url);

		View rootView = inflater.inflate(R.layout.fragment_grid, container,
				false);

		// Hiding the gridview and showing loader image before making the http
		// request
		gridView = (GridView) rootView.findViewById(R.id.grid_view);
		gridView.setVisibility(View.GONE);
		pbLoader = (ProgressBar) rootView.findViewById(R.id.pbLoader);
		pbLoader.setVisibility(View.VISIBLE);

		utils = new Utils(getActivity());

		/**
		 * Making volley's json object request to fetch list of photos of an
		 * album
		 * */
		JsonObjectRequest jsonObjReq = new JsonObjectRequest(Method.GET, url,
				null, new Response.Listener<JSONObject>() {

					@Override
					public void onResponse(JSONObject response) {
						Log.d(TAG,
								"List of photos json reponse: "
										+ response.toString());
						try {
							// Parsing the json response
							JSONArray entry = response.getJSONObject(TAG_FEED)
									.getJSONArray(TAG_ENTRY);

							// looping through each photo and adding it to list
							// data set
							for (int i = 0; i < entry.length(); i++) {
								JSONObject photoObj = (JSONObject) entry.get(i);
								JSONArray mediacontentArry = photoObj
										.getJSONObject(TAG_MEDIA_GROUP)
										.getJSONArray(TAG_MEDIA_CONTENT);

								if (mediacontentArry.length() > 0) {
									JSONObject mediaObj = (JSONObject) mediacontentArry
											.get(0);

									String url = mediaObj
											.getString(TAG_IMG_URL);

									String photoJson = photoObj.getJSONObject(
											TAG_ID).getString(TAG_T)
											+ "&imgmax=d";									

									int width = mediaObj.getInt(TAG_IMG_WIDTH);
									int height = mediaObj
											.getInt(TAG_IMG_HEIGHT);

									Wallpaper p = new Wallpaper(photoJson, url, width,
											height);

									// Adding the photo to list data set
									photosList.add(p);

									Log.d(TAG, "Photo: " + url + ", w: "
											+ width + ", h: " + height);
								}
							}

							// Notify list adapter about dataset changes. So
							// that it renders grid again
							adapter.notifyDataSetChanged();

							// Hide the loader, make grid visible
							pbLoader.setVisibility(View.GONE);
							gridView.setVisibility(View.VISIBLE);

						} catch (JSONException e) {
							e.printStackTrace();
							Toast.makeText(getActivity(),
									getString(R.string.msg_unknown_error),
									Toast.LENGTH_LONG).show();
						}

					}
				}, new Response.ErrorListener() {

					@Override
					public void onErrorResponse(VolleyError error) {
						Log.e(TAG, "Error: " + error.getMessage());
						// unable to fetch wallpapers
						// either google username is wrong or
						// devices doesn't have internet connection
						Toast.makeText(getActivity(),
								getString(R.string.msg_wall_fetch_error),
								Toast.LENGTH_LONG).show();

					}
				});

		// Remove the url from cache
		AppController.getInstance().getRequestQueue().getCache().remove(url);

		// Disable the cache for this url, so that it always fetches updated
		// json
		jsonObjReq.setShouldCache(false);

		// Adding request to request queue
		AppController.getInstance().addToRequestQueue(jsonObjReq);

		// Initilizing Grid View
		InitilizeGridLayout();

		// Gridview adapter
		adapter = new GridViewAdapter(getActivity(), photosList, columnWidth);

		// setting grid view adapter
		gridView.setAdapter(adapter);

		// Grid item select listener
		gridView.setOnItemClickListener(new OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View v,
					int position, long id) {

				// On selecting the grid image, we launch fullscreen activity
				Intent i = new Intent(getActivity(),
						FullScreenViewActivity.class);

				// Passing selected image to fullscreen activity
				Wallpaper photo = photosList.get(position);

				i.putExtra(FullScreenViewActivity.TAG_SEL_IMAGE, photo);
				startActivity(i);
			}
		});

		return rootView;
	}

	/**
	 * Method to calculate the grid dimensions Calculates number columns and
	 * columns width in grid
	 * */
	private void InitilizeGridLayout() {
		Resources r = getResources();
		float padding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
				AppConst.GRID_PADDING, r.getDisplayMetrics());

		// Column width
		columnWidth = (int) ((utils.getScreenWidth() - ((pref
				.getNoOfGridColumns() + 1) * padding)) / pref
				.getNoOfGridColumns());

		// Setting number of grid columns
		gridView.setNumColumns(pref.getNoOfGridColumns());
		gridView.setColumnWidth(columnWidth);
		gridView.setStretchMode(GridView.NO_STRETCH);
		gridView.setPadding((int) padding, (int) padding, (int) padding,
				(int) padding);

		// Setting horizontal and vertical padding
		gridView.setHorizontalSpacing((int) padding);
		gridView.setVerticalSpacing((int) padding);
	}

}

Now go back to your MainActivity.java, uncomment the GridFragment related code which you commented earlier and run the project. You should able to see the wallpapers in GridView once you selected the category from navigation drawer.

android free wallpapers app grid view


Wallpaper Fullscreen View

We also providing user another option to view selected thumbnail image in fullscreen mode. So to implement this, follow below steps.

27. Create an xml file named activity_fullscreen_image.xml under res ⇒ layout folder. This file contains HorizontalScrollView element to scroll the fullscreen image horizontally. Also it contains two more buttons to provide user option to apply the image as wallpaper and download the wallpaper to galleries.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/black" >

    <ProgressBar
        android:id="@+id/pbLoader"
        style="?android:attr/progressBarStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true" >
    </ProgressBar>

    <!-- Scroll view for fullscreen preview -->

    <HorizontalScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="none" >

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="match_parent" >

            <ImageView
                android:id="@+id/imgFullscreen"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:scaleType="fitXY" />
        </LinearLayout>
    </HorizontalScrollView>

    <!-- Set as wallpaper button -->

    <LinearLayout
        android:id="@+id/llSetWallpaper"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_marginBottom="10dp"
        android:layout_marginLeft="10dp"
        android:background="@drawable/btn_rounded_corner"
        android:gravity="center_vertical"
        android:orientation="horizontal" >

        <ImageView
            android:layout_width="25dp"
            android:layout_height="25dp"
            android:src="@drawable/ico_apply" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="10dp"
            android:text="@string/set_wallpaper"
            android:textColor="@color/white"
            android:textSize="18dp" />
    </LinearLayout>

    <!-- Download wallpaper button -->

    <LinearLayout
        android:id="@+id/llDownloadWallpaper"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_marginBottom="10dp"
        android:layout_marginRight="10dp"
        android:background="@drawable/btn_rounded_corner"
        android:gravity="center_vertical"
        android:orientation="horizontal" >

        <ImageView
            android:layout_width="25dp"
            android:layout_height="25dp"
            android:src="@drawable/ico_download" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="10dp"
            android:text="@string/download_wallpaper"
            android:textColor="@color/white"
            android:textSize="18dp" />
    </LinearLayout>

</RelativeLayout>



28. Create a class named FullScreenViewActivity.java under your main package. This class takes care of calculating the image aspect ratio in fullscreen mode. The image width will calculated respective to device height. Below steps will be execute in order show the fullscreen wallpaper.

> User selects the wallpaper from GridView. The selected wallpaper will be passed to fullscreen image activity.
> In fullscreen activity, we make another call to Picasa to get the selected wallpaper’s high resolution version.
> The downloaded wallpaper width will be calculated depending upon device height.
> Then the image will be displayed in ImageView. The HorizontalScrollView make the wallpaper scrollable horizontally.

package info.androidhive.awesomewallpapers;

import info.androidhive.awesomewallpapers.app.AppController;
import info.androidhive.awesomewallpapers.picasa.model.Wallpaper;
import info.androidhive.awesomewallpapers.util.Utils;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.ProgressBar;
import android.widget.Toast;

import com.android.volley.Request.Method;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.ImageLoader.ImageContainer;
import com.android.volley.toolbox.ImageLoader.ImageListener;
import com.android.volley.toolbox.JsonObjectRequest;

public class FullScreenViewActivity extends Activity implements OnClickListener {
	private static final String TAG = FullScreenViewActivity.class
			.getSimpleName();
	public static final String TAG_SEL_IMAGE = "selectedImage";
	private Wallpaper selectedPhoto;
	private ImageView fullImageView;
	private LinearLayout llSetWallpaper, llDownloadWallpaper;
	private Utils utils;
	private ProgressBar pbLoader;

	// Picasa JSON response node keys
	private static final String TAG_ENTRY = "entry",
			TAG_MEDIA_GROUP = "media$group",
			TAG_MEDIA_CONTENT = "media$content", TAG_IMG_URL = "url",
			TAG_IMG_WIDTH = "width", TAG_IMG_HEIGHT = "height";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_fullscreen_image);

		fullImageView = (ImageView) findViewById(R.id.imgFullscreen);
		llSetWallpaper = (LinearLayout) findViewById(R.id.llSetWallpaper);
		llDownloadWallpaper = (LinearLayout) findViewById(R.id.llDownloadWallpaper);
		pbLoader = (ProgressBar) findViewById(R.id.pbLoader);

		// hide the action bar in fullscreen mode
		getActionBar().hide();

		utils = new Utils(getApplicationContext());

		// layout click listeners
		llSetWallpaper.setOnClickListener(this);
		llDownloadWallpaper.setOnClickListener(this);

		// setting layout buttons alpha/opacity
		llSetWallpaper.getBackground().setAlpha(70);
		llDownloadWallpaper.getBackground().setAlpha(70);

		Intent i = getIntent();
		selectedPhoto = (Wallpaper) i.getSerializableExtra(TAG_SEL_IMAGE);

		// check for selected photo null
		if (selectedPhoto != null) {

			// fetch photo full resolution image by making another json request
			fetchFullResolutionImage();

		} else {
			Toast.makeText(getApplicationContext(),
					getString(R.string.msg_unknown_error), Toast.LENGTH_SHORT)
					.show();
		}
	}

	/**
	 * Fetching image fullresolution json
	 * */
	private void fetchFullResolutionImage() {
		String url = selectedPhoto.getPhotoJson();

		// show loader before making request
		pbLoader.setVisibility(View.VISIBLE);
		llSetWallpaper.setVisibility(View.GONE);
		llDownloadWallpaper.setVisibility(View.GONE);

		// volley's json obj request
		JsonObjectRequest jsonObjReq = new JsonObjectRequest(Method.GET, url,
				null, new Response.Listener<JSONObject>() {

					@Override
					public void onResponse(JSONObject response) {
						Log.d(TAG,
								"Image full resolution json: "
										+ response.toString());
						try {
							// Parsing the json response
							JSONObject entry = response
									.getJSONObject(TAG_ENTRY);

							JSONArray mediacontentArry = entry.getJSONObject(
									TAG_MEDIA_GROUP).getJSONArray(
									TAG_MEDIA_CONTENT);

							JSONObject mediaObj = (JSONObject) mediacontentArry
									.get(0);

							String fullResolutionUrl = mediaObj
									.getString(TAG_IMG_URL);

							// image full resolution widht and height
							final int width = mediaObj.getInt(TAG_IMG_WIDTH);
							final int height = mediaObj.getInt(TAG_IMG_HEIGHT);

							Log.d(TAG, "Full resolution image. url: "
									+ fullResolutionUrl + ", w: " + width
									+ ", h: " + height);

							ImageLoader imageLoader = AppController
									.getInstance().getImageLoader();

							// We download image into ImageView instead of
							// NetworkImageView to have callback methods
							// Currently NetworkImageView doesn't have callback
							// methods

							imageLoader.get(fullResolutionUrl,
									new ImageListener() {

										@Override
										public void onErrorResponse(
												VolleyError arg0) {
											Toast.makeText(
													getApplicationContext(),
													getString(R.string.msg_wall_fetch_error),
													Toast.LENGTH_LONG).show();
										}

										@Override
										public void onResponse(
												ImageContainer response,
												boolean arg1) {
											if (response.getBitmap() != null) {
												// load bitmap into imageview
												fullImageView
														.setImageBitmap(response
																.getBitmap());
												adjustImageAspect(width, height);

												// hide loader and show set &
												// download buttons
												pbLoader.setVisibility(View.GONE);
												llSetWallpaper
														.setVisibility(View.VISIBLE);
												llDownloadWallpaper
														.setVisibility(View.VISIBLE);
											}
										}
									});

						} catch (JSONException e) {
							e.printStackTrace();
							Toast.makeText(getApplicationContext(),
									getString(R.string.msg_unknown_error),
									Toast.LENGTH_LONG).show();
						}

					}
				}, new Response.ErrorListener() {

					@Override
					public void onErrorResponse(VolleyError error) {
						Log.e(TAG, "Error: " + error.getMessage());
						// unable to fetch wallpapers
						// either google username is wrong or
						// devices doesn't have internet connection
						Toast.makeText(getApplicationContext(),
								getString(R.string.msg_wall_fetch_error),
								Toast.LENGTH_LONG).show();

					}
				});

		// Remove the url from cache
		AppController.getInstance().getRequestQueue().getCache().remove(url);

		// Disable the cache for this url, so that it always fetches updated
		// json
		jsonObjReq.setShouldCache(false);

		// Adding request to request queue
		AppController.getInstance().addToRequestQueue(jsonObjReq);
	}

	/**
	 * Adjusting the image aspect ration to scroll horizontally, Image height
	 * will be screen height, width will be calculated respected to height
	 * */
	@SuppressWarnings("deprecation")
	@SuppressLint("NewApi")
	private void adjustImageAspect(int bWidth, int bHeight) {
		LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
				LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);

		if (bWidth == 0 || bHeight == 0)
			return;

		int sHeight = 0;

		if (android.os.Build.VERSION.SDK_INT >= 13) {
			Display display = getWindowManager().getDefaultDisplay();
			Point size = new Point();
			display.getSize(size);
			sHeight = size.y;
		} else {
			Display display = getWindowManager().getDefaultDisplay();
			sHeight = display.getHeight();
		}

		int new_width = (int) Math.floor((double) bWidth * (double) sHeight
				/ (double) bHeight);
		params.width = new_width;
		params.height = sHeight;

		Log.d(TAG, "Fullscreen image new dimensions: w = " + new_width
				+ ", h = " + sHeight);

		fullImageView.setLayoutParams(params);
	}

	/**
	 * View click listener
	 * */
	@Override
	public void onClick(View v) {
		Bitmap bitmap = ((BitmapDrawable) fullImageView.getDrawable())
				.getBitmap();
		switch (v.getId()) {
		// button Download Wallpaper tapped
		case R.id.llDownloadWallpaper:
			utils.saveImageToSDCard(bitmap);
			break;
		// button Set As Wallpaper tapped
		case R.id.llSetWallpaper:
			utils.setAsWallpaper(bitmap);
			break;
		default:
			break;
		}

	}
}



29. Add the FullScreenViewActivity entry in AndroidManifest.xml file between <application> tags.

<application ...>       
        <activity
            android:name="info.androidhive.awesomewallpapers.FullScreenViewActivity"
            android:screenOrientation="portrait" >
        </activity>
        </activity>
</application>

Now run the project and select any image from grid view. You should able see fullscreen image activity launched with wallpaper image preview.

android free wallpapers app grid view


Providing Set as Wallpaper Option

30. To apply the wallpaper to device, add a click listener to set as wallpaper button and call setAsWallpaper() method presented in Utils.java class

Add below line in onCreate() method of FullScreenViewActivity.java

llSetWallpaper.setOnClickListener(this);

Call below method in onClick() method of FullScreenViewActivity.java

	/**
	 * View click listener
	 * */
	@Override
	public void onClick(View v) {
		Bitmap bitmap = ((BitmapDrawable) fullImageView.getDrawable())
				.getBitmap();
		switch (v.getId()) {		
		case R.id.llSetWallpaper:
			utils.setAsWallpaper(bitmap);
			break;
		default:
			break;
		}

	}

Now if you tap the Set button, the wallpaper will be applied to device.

android free wallpapers set as wallpaper


Downloading Wallpaper to Gallery

31. Just like set as wallpaper, to download wallpaper to device gallery directory, add the click listener to download button and call saveImageToSDCard() method presented in Utils.java

Add below line in onCreate() method of FullScreenViewActivity.java

llDownloadWallpaper.setOnClickListener(this);

Add another case statement in onClick() method of FullScreenViewActivity.java

	/**
	 * View click listener
	 * */
	@Override
	public void onClick(View v) {
		Bitmap bitmap = ((BitmapDrawable) fullImageView.getDrawable())
				.getBitmap();
		switch (v.getId()) {
		// button Download Wallpaper tapped
		case R.id.llDownloadWallpaper:
			utils.saveImageToSDCard(bitmap);
			break;
		// button Set As Wallpaper tapped
		case R.id.llSetWallpaper:
			utils.setAsWallpaper(bitmap);
			break;
		default:
			break;
		}

	}

Now if you tap Download Wallpaper button, the wallpaper will be downloaded to your gallery with album named Awesome Wallpapers. If you don’t see the album there, reboot your device. I couldn’t see the album created until I rebooted my device. I am yet to find the issue :(


With this we almost the completed the app. But lastly we need to add a Settings screen where user can configure Picasa username, number of grid columns and gallery directory name. This step is not necessary if you are planning to release this app in playstore. It is just for the developers who is reading this.

Adding Settings Screen

32. . Create a layout file named activity_settings.xml under res layout folder. This layout contains a form with few edittext fields and a save button.

<?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="match_parent"
    android:orientation="vertical"
    android:padding="10dp" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:layout_marginTop="15dp"
        android:text="@string/lbl_google_username"
        android:textColor="@color/settings_label"
        android:textSize="16dp" />

    <EditText
        android:id="@+id/txtGoogleUsername"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="20dp"
        android:background="@drawable/edittext_rounded_corner"
        android:inputType="text"
        android:singleLine="true" />

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:text="@string/lbl_no_grid_columns"
        android:textColor="@color/settings_label"
        android:textSize="16dp" />

    <EditText
        android:id="@+id/txtNoOfColumns"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="20dp"
        android:background="@drawable/edittext_rounded_corner"
        android:inputType="number"
        android:singleLine="true" />

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:text="@string/lbl_gallery_name"
        android:textColor="@color/settings_label"
        android:textSize="16dp" />

    <EditText
        android:id="@+id/txtGalleryName"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="25dp"
        android:background="@drawable/edittext_rounded_corner"
        android:singleLine="true" />

    <Button
        android:id="@+id/btnSave"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/btn_rounded_corner"
        android:paddingLeft="30dp"
        android:paddingRight="30dp"
        android:text="@string/lbl_btn_save"
        android:textColor="@color/white"
        android:textSize="18dp" />

</LinearLayout>



33. . Create a class named SettingsActivity.java and add the below code. This code just validates the data entered by user and overwrites the respective values in Shared Preferences.

package info.androidhive.awesomewallpapers;

import info.androidhive.awesomewallpapers.R;
import info.androidhive.awesomewallpapers.util.PrefManager;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class SettingsActivity extends Activity {
	private PrefManager pref;
	private TextView txtGoogleUsername, txtNoOfGridColumns, txtGalleryName;
	private Button btnSave;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_settings);

		txtGoogleUsername = (TextView) findViewById(R.id.txtGoogleUsername);
		txtNoOfGridColumns = (TextView) findViewById(R.id.txtNoOfColumns);
		txtGalleryName = (TextView) findViewById(R.id.txtGalleryName);
		btnSave = (Button) findViewById(R.id.btnSave);

		pref = new PrefManager(getApplicationContext());

		// Display edittext values stored in shared preferences
		// Google username
		txtGoogleUsername.setText(pref.getGoogleUserName());

		// Number of grid columns
		txtNoOfGridColumns.setText(String.valueOf(pref.getNoOfGridColumns()));

		// Gallery name
		txtGalleryName.setText(pref.getGalleryName());

		// Save settings button click listener
		btnSave.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {

				// Validating the data before saving to shared preferences
				// validate google username
				String googleUsername = txtGoogleUsername.getText().toString()
						.trim();
				if (googleUsername.length() == 0) {
					Toast.makeText(getApplicationContext(),
							getString(R.string.toast_enter_google_username),
							Toast.LENGTH_LONG).show();
					return;
				}

				// validate number of grid columns
				String no_of_columns = txtNoOfGridColumns.getText().toString()
						.trim();
				if (no_of_columns.length() == 0 || !isInteger(no_of_columns)) {
					Toast.makeText(getApplicationContext(),
							getString(R.string.toast_enter_valid_grid_columns),
							Toast.LENGTH_LONG).show();
					return;
				}

				// validate gallery name
				String galleryName = txtGalleryName.getText().toString().trim();
				if (galleryName.length() == 0) {
					Toast.makeText(getApplicationContext(),
							getString(R.string.toast_enter_gallery_name),
							Toast.LENGTH_LONG).show();
					return;
				}

				// Check for setting changes
				if (!googleUsername.equalsIgnoreCase(pref.getGoogleUserName())
						|| !no_of_columns.equalsIgnoreCase(String.valueOf(pref
								.getNoOfGridColumns()))
						|| !galleryName.equalsIgnoreCase(pref.getGalleryName())) {
					// User changed the settings
					// save the changes and launch SplashScreen to initialize
					// the app again
					pref.setGoogleUsername(googleUsername);
					pref.setNoOfGridColumns(Integer.parseInt(no_of_columns));
					pref.setGalleryName(galleryName);

					// start the app from SplashScreen
					Intent i = new Intent(SettingsActivity.this,
							SplashActivity.class);
					// Clear all the previous activities
					i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
							| Intent.FLAG_ACTIVITY_CLEAR_TASK);
					startActivity(i);
				} else {
					// user not modified any values in the form
					// skip saving to shared preferences
					// just go back to previous activity
					onBackPressed();
				}

			}
		});

	}

	public boolean isInteger(String input) {
		try {
			Integer.parseInt(input);
			return true;
		} catch (Exception e) {
			return false;
		}
	}
}

34. Finally add settings activity entry in AndroidManifest.xml

    <application ...>        
        <activity
            android:name="info.androidhive.awesomewallpapers.SettingsActivity"
            android:label="@string/action_settings"
            android:screenOrientation="portrait" >
        </activity>
    </application>

Finally your AndroidManifest.xml file should look like this.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidhive.awesomewallpapers"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="19" />

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

    <application
        android:name="info.androidhive.awesomewallpapers.app.AppController"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/FreeWallTheme" >
        <activity
            android:name="info.androidhive.awesomewallpapers.SplashActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="info.androidhive.awesomewallpapers.MainActivity"
            android:screenOrientation="portrait" >
        </activity>
        <activity
            android:name="info.androidhive.awesomewallpapers.FullScreenViewActivity"
            android:screenOrientation="portrait" >
        </activity>
        <activity
            android:name="info.androidhive.awesomewallpapers.SettingsActivity"
            android:label="@string/action_settings"
            android:screenOrientation="portrait" >
        </activity>
    </application>

</manifest>

Run the app and launch settings screen by tapping overflow icon on action bar.


Congratulations :) If you are succeeded all the steps until now, you just create a fully fledged wallpapers app which has good potential go into market right away. But before that I would like to address few important points to you.

> This app is doesn’t supports older versions < 4.0. For that you need to use Android Support Library to provide backward compatibility.

> Even though we are using Google+ / Picasa web services to provide wallpapers, in real scenario we should have a server side technology to detect the mobile device and respond the wallpapers depending on mobile configuration like screen dimensions.

If you have any queries or suggestions please leave in comment section below.

]]>
http://www.androidhive.info/2014/08/android-building-free-wallpapers-app-part-2/feed/ 0
Android Building Free Wallpapers App – Part 1http://www.androidhive.info/2014/08/android-building-free-wallpapers-app-part-1/ http://www.androidhive.info/2014/08/android-building-free-wallpapers-app-part-1/#comments Wed, 20 Aug 2014 05:50:01 +0000 http://www.androidhive.info/?p=8944 Today I am going to show to how to build a free wallpapers app using Picasa Web Albums API. In this tutorial you can learn how to integrate multiple android components like splash screen, navigation drawer, grid view, volley library in a single app.

As this tutorial seems very lengthy, I divided this into two parts. In 1st part we do all basic setup like creating the Picasa albums, planning the app design and creating the android project and write some reusable components required for the app. In the 2nd part the actual implementation of the app starts where we add one by one module to the app like splash screen, navigation drawer, grid view and other things to complete the app.

android free wallpapers app part 2


1. Prerequisite

As this app is a complete application we need to integrate several components together. Please go through below articles before getting into this tutorial as these concepts are main building blocks for this app.

1. How to implement Android Splash Screen This explains how to add a splash screen to your app when the app needs to make few HTTP calls and fetch some data before going in to actual app flow.

2. Android Sliding Menu using Navigation Drawer This article helps in adding a slider menu for the app where we can show list of categories for wallpapers.

3. Android working with Volley Library We use volley to make network calls like downloading the json and images. Also another main advantage of using volley is image cache which makes the app faster.

4. Android Full Screen Image Slider with Swipe and Pinch Zoom Gestures This is another important article helps in creating the app’s main Grid View screen where the thumbnail preview of each wallpaper will be displayed in a grid fashion.

2. Google’s Picasa Web Albums API

The major and difficult task in this app is building the server side components like an admin panel to manage your wallpaper data by providing file upload option and other things. For this you should also able to code in other technologies like HTML, CSS, JSP, PHP or MySQL which is again a difficult task for android beginners.

So I selected Picasa Web Albums as a robust solution for this. Picasa web albums is free of cost and importantly it exposes an API (json and xml) for developers. You can go through Picasa Web Albums Data API to know more about the API’s exposed.

We are specifically interested in following API calls for this app which doesn’t require any authentication as we perform read operations only on public albums.

1. Request a list of albums
(username should be replaced with your Google username)

https://picasaweb.google.com/data/feed/api/user/username?kind=album&alt=json

2. List photos recently uploaded
(username should be replaced with your Google username)

https://picasaweb.google.com/data/feed/api/user/username?kind=photo&alt=json

3. List photos in album
(albumid should be replaced with actual album id which can find in albums response)

https://picasaweb.google.com/data/feed/api/user/username/albumid/albumid?alt=json

If you want to provide more options like providing user to comment, edit or delete options you need to implement authentication as mentioned in the document.

Hint: alt parameter decides the type of response we are expecting either json or xml. Pass alt=json in the url to get the response in JSON format.


3. Adding wallpapers in Picasa Web Albums

Before starting the app, we’ll add few wallpapers in Picasa albums for testing. You can login into Picasa using your Google account. Follow below steps to create albums and wallpapers in Picasa web albums.

1. Login into Picasa.
(If you are redirected to your Google+ account, click on Click here to go back to Picasa Web Albums to navigate back to Picasa Web or go with Google+ if you are comfortable)

2. Click on upload and enter your album name. These albums act as wallpaper categories in our app which will be listed in navigation drawer menu. Example: Animals, Cars, Food, Movies, Vintage etc.,

3. Once you created the album, upload few wallpapers to the album. Make sure that you are uploading the wallpapers with good high resolution.

4. Now make the album visibility as Public by clicking on Edit link displayed on the right. Note: If you don’t make your album as public, it will not be accessible via your android app. In other words you can’t see the album in the json response.

5. Follow 2nd, 3rd and 4th steps to create few more albums and wallpapers.

Checkout the below video which takes you through uploading process.



4. Designing The App

The design of this app is very minimalistic with very few color combinations. Overall it contains following components.

1. Navigation drawer (Slider Menu) to display wallpaper categories.
2. Grid view screen to display the thumbnail of wallpapers under a category.
3. Full screen view of wallpaper with Set As Wallpaper and Download Wallpaper options.
4. Settings screen to configure Picasa username, number of columns in grid and gallery image directory.

Below are screen shots of the final app.

Splash Screen

android freewallpapers app splash screen

Navigation Drawer

android wallpapers navigation drawer

Wallpapers thumbnail view

android wallpapers app grid view

Full screen view with Set As Wallpaper and Download options

android wallpapers app fullscreen view

Settings view

android wallpapers settings screen


5. Downloading Volley Library (volley.jar)

To make calls to Picasa API we are going to use Volley Library instead of using the java library mentioned in the documentation. I already published an article about Volley Library explaining about the advantages of choosing volley. In this project it plays a major role in caching the images.

Download the precompiled volley.jar



Now it’s time to start the android app. I am naming this app as Awesome Wallpapers.

6. Creating New Project

1. In Eclipse create a new project by navigating to File ⇒ New ⇒ Android Application Project and fill required details. I gave my project package name as info.androidhive.awesomewallpapers

2. Once the project is created paste the volley.jar in libs folder.

3. Download the resources and place them in your project’s respective folders. In this downloaded zip you will find splash screen background image and other app icons.

4. Now add below resource values in strings.xml, colors.xml. These values are used in various places in the project.

strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">Awesome Wallpapers</string>
    <string name="action_settings">Settings</string>
    <string name="nav_drawer_recently_added">Recently Added</string>
    <string name="toast_saved">Wallpapaer saved to #</string>
    <string name="toast_saved_failed">Sorry! Failed to save wallpaper</string>
    <string name="toast_wallpaper_set">Wallpapaer updated!</string>
    <string name="toast_wallpaper_set_failed">Sorry! Unable to set wallpapaer</string>
    <string name="splash_error">Unable to download wallpapers. Verify your google\'s user name in settings or device doesn\'t have internet connection!</string>
    <string name="set_wallpaper">Set</string>
    <string name="download_wallpaper">Download</string>
    <string name="msg_unknown_error">Sorry! Unknown error occurred.</string>
    <string name="msg_wall_fetch_error">Sorry! Unable to fetch wallpaper(s). Verify app settings or device doesn\'t have internet connection.</string>
    <string name="toast_enter_google_username">Please enter google username</string>
    <string name="toast_enter_valid_grid_columns">Please enter valid number of columns</string>
    <string name="toast_enter_gallery_name">Please enter gallery directory name</string>
    <string name="lbl_google_username">Google username</string>
    <string name="lbl_no_grid_columns">Number of grid columns</string>
    <string name="lbl_gallery_name">Gallery directory name</string>
    <string name="lbl_btn_save">Save</string>

</resources>

colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <color name="list_item_title">#d7ddee</color>
    <color name="list_background">#1a1b1d</color>
    <color name="list_background_pressed">#131315</color>
    <color name="list_divider">#272727</color>
    <color name="counter_text_bg">#626262</color>
    <color name="counter_text_color">#c5c4c4</color>
    <color name="black">#000000</color>
    <color name="grid_bg">#000000</color>
    <color name="grid_item_bg">#1a1b1d</color>
    <color name="action_bar">#141416</color>
    <color name="action_bar_title">#ffffff</color>
    <color name="white">#ffffff</color>
    <color name="settings_label">#323232</color>

</resources>

5. Now create five packages named app, helper, util and picasa.model under src folder. Right Click on src ⇒ New ⇒ Package.

info.androidhive.awesomewallpapers.app
info.androidhive.awesomewallpapers.helper
info.androidhive.awesomewallpapers.picasa.model
info.androidhive.awesomewallpapers.util

6. Now create AppConst.java in app package. All the app configuration like google username, picasa api urls, gallery directory name and other static variables goes in this class.

package info.androidhive.awesomewallpapers.app;


public class AppConst {

	// Number of columns of Grid View
	// by default 2 but user can configure this in settings activity
	public static final int NUM_OF_COLUMNS = 2;

	// Gridview image padding
	public static final int GRID_PADDING = 4; // in dp

	// Gallery directory name to save wallpapers
	public static final String SDCARD_DIR_NAME = "Awesome Wallpapers";

	// Picasa/Google web album username
	public static final String PICASA_USER = "freewallpapersapp";

	// Public albums list url
	public static final String URL_PICASA_ALBUMS = "https://picasaweb.google.com/data/feed/api/user/_PICASA_USER_?kind=album&alt=json";

	// Picasa album photos url
	public static final String URL_ALBUM_PHOTOS = "https://picasaweb.google.com/data/feed/api/user/_PICASA_USER_/albumid/_ALBUM_ID_?alt=json";

	// Picasa recenlty added photos url
	public static final String URL_RECENTLY_ADDED = "https://picasaweb.google.com/data/feed/api/user/_PICASA_USER_?kind=photo&alt=json";

}



7. Create another class named AppController.java under app package. This class extends from Application and this is a singleton class which initializes volley’s core objects and few other classes those can be used across the app.

package info.androidhive.awesomewallpapers.app;

import info.androidhive.awesomewallpapers.util.LruBitmapCache;
import info.androidhive.awesomewallpapers.util.PrefManager;
import android.app.Application;
import android.text.TextUtils;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;

public class AppController extends Application {

	public static final String TAG = AppController.class.getSimpleName();

	private RequestQueue mRequestQueue;
	private ImageLoader mImageLoader;
	LruBitmapCache mLruBitmapCache;

	private static AppController mInstance;
	private PrefManager pref;

	@Override
	public void onCreate() {
		super.onCreate();
		mInstance = this;
		pref = new PrefManager(this);
	}

	public static synchronized AppController getInstance() {
		return mInstance;
	}

	public PrefManager getPrefManger() {
		if (pref == null) {
			pref = new PrefManager(this);
		}

		return pref;
	}

	public RequestQueue getRequestQueue() {
		if (mRequestQueue == null) {
			mRequestQueue = Volley.newRequestQueue(getApplicationContext());
		}

		return mRequestQueue;
	}

	public ImageLoader getImageLoader() {
		getRequestQueue();
		if (mImageLoader == null) {
			getLruBitmapCache();
			mImageLoader = new ImageLoader(this.mRequestQueue, mLruBitmapCache);
		}

		return this.mImageLoader;
	}

	public LruBitmapCache getLruBitmapCache() {
		if (mLruBitmapCache == null)
			mLruBitmapCache = new LruBitmapCache();
		return this.mLruBitmapCache;
	}

	public <T> void addToRequestQueue(Request<T> req, String tag) {
		req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
		getRequestQueue().add(req);
	}

	public <T> void addToRequestQueue(Request<T> req) {
		req.setTag(TAG);
		getRequestQueue().add(req);
	}

	public void cancelPendingRequests(Object tag) {
		if (mRequestQueue != null) {
			mRequestQueue.cancelAll(tag);
		}
	}
}



8. Create Utils.java under util package. This class contains few helper methods.

package info.androidhive.awesomewallpapers.util;

import info.androidhive.awesomewallpapers.R;

import java.io.File;
import java.io.FileOutputStream;
import java.util.Random;

import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.os.Environment;
import android.util.Log;
import android.view.Display;
import android.view.WindowManager;
import android.widget.Toast;

public class Utils {
	private String TAG = Utils.class.getSimpleName();
	private Context _context;
	private PrefManager pref;

	// constructor
	public Utils(Context context) {
		this._context = context;
		pref = new PrefManager(_context);
	}

	/*
	 * getting screen width
	 */
	@SuppressWarnings("deprecation")
	public int getScreenWidth() {
		int columnWidth;
		WindowManager wm = (WindowManager) _context
				.getSystemService(Context.WINDOW_SERVICE);
		Display display = wm.getDefaultDisplay();

		final Point point = new Point();
		try {
			display.getSize(point);
		} catch (java.lang.NoSuchMethodError ignore) {
			// Older device
			point.x = display.getWidth();
			point.y = display.getHeight();
		}
		columnWidth = point.x;
		return columnWidth;
	}

	public void saveImageToSDCard(Bitmap bitmap) {
		File myDir = new File(
				Environment
						.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
				pref.getGalleryName());

		myDir.mkdirs();
		Random generator = new Random();
		int n = 10000;
		n = generator.nextInt(n);
		String fname = "Wallpaper-" + n + ".jpg";
		File file = new File(myDir, fname);
		if (file.exists())
			file.delete();
		try {
			FileOutputStream out = new FileOutputStream(file);
			bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
			out.flush();
			out.close();
			Toast.makeText(
					_context,
					_context.getString(R.string.toast_saved).replace("#",
							"\"" + pref.getGalleryName() + "\""),
					Toast.LENGTH_SHORT).show();
			Log.d(TAG, "Wallpaper saved to: " + file.getAbsolutePath());

		} catch (Exception e) {
			e.printStackTrace();
			Toast.makeText(_context,
					_context.getString(R.string.toast_saved_failed),
					Toast.LENGTH_SHORT).show();
		}
	}

	public void setAsWallpaper(Bitmap bitmap) {
		try {
			WallpaperManager wm = WallpaperManager.getInstance(_context);

			wm.setBitmap(bitmap);
			Toast.makeText(_context,
					_context.getString(R.string.toast_wallpaper_set),
					Toast.LENGTH_SHORT).show();
		} catch (Exception e) {
			e.printStackTrace();
			Toast.makeText(_context,
					_context.getString(R.string.toast_wallpaper_set_failed),
					Toast.LENGTH_SHORT).show();
		}
	}
}



9. Create LruBitmapCache.java and paste the below code. This class used to keep the volley cached objects on the disk.

package info.androidhive.awesomewallpapers.util;

import com.android.volley.toolbox.ImageLoader.ImageCache;

import android.graphics.Bitmap;
import android.support.v4.util.LruCache;

public class LruBitmapCache extends LruCache<String, Bitmap> implements
		ImageCache {
	public static int getDefaultLruCacheSize() {
		final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
		final int cacheSize = maxMemory / 8;

		return cacheSize;
	}

	public LruBitmapCache() {
		this(getDefaultLruCacheSize());
	}

	public LruBitmapCache(int sizeInKiloBytes) {
		super(sizeInKiloBytes);
	}

	@Override
	protected int sizeOf(String key, Bitmap value) {
		return value.getRowBytes() * value.getHeight() / 1024;
	}

	@Override
	public Bitmap getBitmap(String url) {
		return get(url);
	}

	@Override
	public void putBitmap(String url, Bitmap bitmap) {
		put(url, bitmap);
	}
}



10. Create two classes named Category.java and Wallpaper.java under picasa.model package. These POJO classes will be useful while parsing the json data fetched from Picasa.

package info.androidhive.awesomewallpapers.picasa.model;

public class Category {
	public String id, title;

	public Category() {		
	}

	public Category(String id, String title) {		
		this.id = id;
		this.title = title;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}
}
package info.androidhive.awesomewallpapers.picasa.model;

import java.io.Serializable;

public class Wallpaper implements Serializable {
	private static final long serialVersionUID = 1L;
	private String url, photoJson;
	private int width, height;

	public Wallpaper() {
	}

	public Wallpaper(String photoJson, String url, int width, int height) {		
		this.photoJson = photoJson;
		this.url = url;
		this.width = width;
		this.height = height;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getPhotoJson() {
		return photoJson;
	}

	public void setPhotoJson(String photoJson) {
		this.photoJson = photoJson;
	}

	public int getWidth() {
		return width;
	}

	public void setWidth(int width) {
		this.width = width;
	}

	public int getHeight() {
		return height;
	}

	public void setHeight(int height) {
		this.height = height;
	}
}



11. Finally create another class named PrefManager.java under utils package. This class takes care of storing the data in Shared Preferences. The data like google username, picasa wallpaper categories, gallery name and other things will be stored in shared preferences.

package info.androidhive.awesomewallpapers.util;

import info.androidhive.awesomewallpapers.app.AppConst;
import info.androidhive.awesomewallpapers.picasa.model.Category;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.util.Log;

import com.google.gson.Gson;

public class PrefManager {
	private static final String TAG = PrefManager.class.getSimpleName();

	// Shared Preferences
	SharedPreferences pref;

	// Editor for Shared preferences
	Editor editor;

	// Context
	Context _context;

	// Shared pref mode
	int PRIVATE_MODE = 0;

	// Sharedpref file name
	private static final String PREF_NAME = "AwesomeWallpapers";

	// Google's username
	private static final String KEY_GOOGLE_USERNAME = "google_username";

	// No of grid columns
	private static final String KEY_NO_OF_COLUMNS = "no_of_columns";

	// Gallery directory name
	private static final String KEY_GALLERY_NAME = "gallery_name";

	// gallery albums key
	private static final String KEY_ALBUMS = "albums";

	public PrefManager(Context context) {
		this._context = context;
		pref = _context.getSharedPreferences(PREF_NAME, PRIVATE_MODE);

	}

	/**
	 * Storing google username
	 * */
	public void setGoogleUsername(String googleUsername) {
		editor = pref.edit();

		editor.putString(KEY_GOOGLE_USERNAME, googleUsername);

		// commit changes
		editor.commit();
	}

	public String getGoogleUserName() {
		return pref.getString(KEY_GOOGLE_USERNAME, AppConst.PICASA_USER);
	}

	/**
	 * store number of grid columns
	 * */
	public void setNoOfGridColumns(int columns) {
		editor = pref.edit();

		editor.putInt(KEY_NO_OF_COLUMNS, columns);

		// commit changes
		editor.commit();
	}

	public int getNoOfGridColumns() {
		return pref.getInt(KEY_NO_OF_COLUMNS, AppConst.NUM_OF_COLUMNS);
	}

	/**
	 * storing gallery name
	 * */
	public void setGalleryName(String galleryName) {
		editor = pref.edit();

		editor.putString(KEY_GALLERY_NAME, galleryName);

		// commit changes
		editor.commit();
	}

	public String getGalleryName() {
		return pref.getString(KEY_GALLERY_NAME, AppConst.SDCARD_DIR_NAME);
	}

	/**
	 * Storing albums in shared preferences
	 * */
	public void storeCategories(List<Category> albums) {
		editor = pref.edit();
		Gson gson = new Gson();

		Log.d(TAG, "Albums: " + gson.toJson(albums));

		editor.putString(KEY_ALBUMS, gson.toJson(albums));

		// save changes
		editor.commit();
	}

	/**
	 * Fetching albums from shared preferences. Albums will be sorted before
	 * returning in alphabetical order
	 * */
	public List<Category> getCategories() {
		List<Category> albums = new ArrayList<Category>();

		if (pref.contains(KEY_ALBUMS)) {
			String json = pref.getString(KEY_ALBUMS, null);
			Gson gson = new Gson();
			Category[] albumArry = gson.fromJson(json, Category[].class);

			albums = Arrays.asList(albumArry);
			albums = new ArrayList<Category>(albums);
		} else
			return null;

		List<Category> allAlbums = albums;

		// Sort the albums in alphabetical order
		Collections.sort(allAlbums, new Comparator<Category>() {
			public int compare(Category a1, Category a2) {
				return a1.getTitle().compareToIgnoreCase(a2.getTitle());
			}
		});

		return allAlbums;

	}

	/**
	 * Comparing albums titles for sorting
	 * */
	public class CustomComparator implements Comparator<Category> {
		@Override
		public int compare(Category c1, Category c2) {
			return c1.getTitle().compareTo(c2.getTitle());
		}
	}
}



Now we have all the required classes in place. So let’s start adding one by one component to the app in Android Building Free Wallpapers App – Part 2

]]>
http://www.androidhive.info/2014/08/android-building-free-wallpapers-app-part-1/feed/ 0
3 Ways to Manage Android SD Card and Phone Memoryhttp://www.androidhive.info/2014/07/3-ways-to-manage-android-sd-card-and-phone-memory/ http://www.androidhive.info/2014/07/3-ways-to-manage-android-sd-card-and-phone-memory/#comments Wed, 30 Jul 2014 18:24:50 +0000 http://www.androidhive.info/?p=17100 Android open nature makes it extremely easy for you to have access to the Android SD card. Just plug in a USB cable and you begin to manage music, video, photos, document files on it. It’s really convenient. However, you’re very clear that it can’t do everything. What of you want to manage contacts, messages, calendars? What of you decide to backup app data before resetting your phone? What if you get a Mac and want to manage Android SD card on it, but Mac doesn’t detect the Android phone? In this case, you need to draw support from some third-party tool. Today, in this article, I’d like to show you 3 ways to manage Android SD card and phone memory. Just read on.

Wondershare MobileGo

Manage Android SD Card and Phone Memory from PC

One way to manage Android SD card and Phone memory from windows PC is by mounting your Android phone to use it as an external hard drive. Plug your phone in, and set it in USB storage mode. This way you can use your phone as a flash drive. You can pull your photos and videos off.

9

The process of mounting varies from phone to phone, but the basic principal remains same. Just a few simple steps:

1. Use the USB cable that you receive with your phone. Plug the one end of the cable into your phone and other in your PC. Let the windows install required drivers if you are doing this for the first time. It may take a few minutes.

6

2. On your device, pull the notification bar down and tap on USB connected. You will be asked to “Mount” in order to copy files between the computer and phone. Choose “Mount.”

8

3. Now, Go to “My Computer.” Your storage card will appear as one of the options in “My Computer.” The storage card shows up as a separate drive. As shown below as “F” drive. Now, you can easily copy files from and to your storage card.

5

Tip:

Make sure you unmount or eject the storage card from your computer. In the list of drives, right-click and choose “Eject.” Pull the notification bar down on your phone and select “Turnoff USB storage.” Choose “Turnoff,” and you’re done.

11
12

Advantages:

• Easy and quick backup
• No charge

Disadvantages:

Can’t backup applications, and application data

Use Android File Transfer to manage Android SD card on windows

Wondershare MobileGo Android SD card manager serve as the best tool to export and secure data on your desktop. Wondershare MobileGo for Android is the most reliable, convenient and user-friendly tool. With its advanced features, it supports the wide range of devices including tablets.

Features:

1. It converts the PC into an Android phone and facilitates instant sending or reply to the SMS.
2. It facilitates export and import of countless media files.
3. It facilitates auto-updating of your contacts and facilitates to and from outlook transportation. It facilitates synchronisation of thousands of contacts in Outlook.
4. It syncs and transfers data at bullet speed.

How to use Wondershare MobileGo to manage SD card and phone memory:

Step 1: Install Wondershare MobileGo for Android on the Windows computer.

Step 2: Run it and connect your Android phone or tablet to the Windows computer via Wi-Fi or USB cable.

Step 3: Your Android phone or tablet will be quickly detected and then displayed in the primary window.

Step 4: In the primary window, click Files in the left column. Then, on the right panel, click SD Card. Then, all folders and files on SD card are shown. Select your wanted folders and files and then click Export.

4

To backup app, app data and other files, you can follow this way: In the primary window, go to Tools you may like. Click One-click Backup. In the pop-up backup dialog, check the files you want to backup: contacts, text messages, app, app data, calendars, call logs, photos, music and videos. Then, click Backup to backup them.

3

Advantages:

• User friendly Interface.
• Easy date restore

Disadvantages:

• No auto- synchronisation of photos with Outlook.


Manage Android SD card on Mac with Wondershare MobileGo

MobileGo for Android Pro (Mac) offers a hassle- free way to let you backup everything on your Android phone. You can also restore and backed-up files to your Android phone with just one click. You can manage all the data on your SD card with this application.

2

To Backup/Restore data:
Connect your Android device with a USB cable. Launch the program, and you’ll see its main interface. At the home screen, the options to backup and restore are both located. Click the option you want to use.

To backup data:
i. Click on the Backup button and open the following Back Up window.
ii. Select the contents you want to copy as contacts, SMS, Apps, etc.
iii. Click on “Browse” to select a folder to save the files.
iv. To start the process, click on the backup. No Hassle! It is done quickly
and easily.

1

To restore data:
i. Click on the “Restore button” and it will open the Restore windows.
ii. In restore window, you can select the files and folders you want to restore to your device.
iii. Click “Restore” to complete the process.

10

Advantages:

• Easy to use
• Quick processing

Disadvantages:

Limited access in free version

]]>
http://www.androidhive.info/2014/07/3-ways-to-manage-android-sd-card-and-phone-memory/feed/ 0
Android Custom ListView with Image and Text using Volleyhttp://www.androidhive.info/2014/07/android-custom-listview-with-image-and-text-using-volley/ http://www.androidhive.info/2014/07/android-custom-listview-with-image-and-text-using-volley/#comments Mon, 28 Jul 2014 10:47:22 +0000 http://www.androidhive.info/?p=16408 My previous article Customized ListView with Image and Text gives you a good overview of customizing a list view which contains a thumbnail image and few text fields. All the list data will be downloaded by making a network calls. The main challenge in that tutorial is downloading the images asynchronously and caching them. Because of lack of good tools I used a third party library (it is a good library though) to download the listview data and caching the images.

Today I am going to explain the same, but this time with a different approach by using an another library called volley. By using volley you can see decent improvement in listview performance without much effort.

Android Custom ListView with Image and Text using Volley


Final Output

The list view we are going to build contains list of movies information where each list row defines single movie. The row layout is customized that contains movie poster as thumbnail image on the left and the movie title, rating, genre and release date on the right. Below is the final output.

android custom listview with image and text using volley

1. Example JSON

I am going to use this example json to load the list data. This json contains array of objects where the each object contains information like movie name, rating, genre and release date.

JSON Url: http://api.androidhive.info/json/movies.json

[
    {
        "title": "Dawn of the Planet of the Apes",
        "image": "http://api.androidhive.info/json/movies/1.jpg",
        "rating": 8.3,
        "releaseYear": 2014,
        "genre": ["Action", "Drama", "Sci-Fi"]
    },
    ....
    ....
]


2. Downloading Volley Library (volley.jar)

If you are coming across volley for the first time, I would suggest you go through my previous article Android working with Volley Library which gives you enough knowledge about volley and other things like building the volley library to generate volley.jar. It is up to you that you want to generate volley.jar on your own or just download the the volley.jar

3. Planning The Layout Design

To achieve this customized layout I choose the RelativeLayout as parent element. All the child elements are aligned using relative properties like align below, align right, align parent bottom etc.,

android custom list view design



Now let’s start by creating a new project.

4. Creating New Project

1. In Eclipse create a new project by navigating to File ⇒ New ⇒ Android Application Project and fill required details. I gave my project package name as
info.androidhive.customlistviewvolley

2. Once the project is created paste the volley.jar in libs folder.

3. I am creating required packages first just to keep the project organised. This step is optional but it is recommended. Create four packages named adapter, app, model and util. So after creating packages my project contains following.
info.androidhive.customlistviewvolley.adater
info.androidhive.customlistviewvolley.app
info.androidhive.customlistviewvolley.model
info.androidhive.customlistviewvolley.util

4. Open colors.xml under res ⇒ values and add below colors. If you don’t see colors.xml file, create a new file and add these color values.

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <color name="genre">#666666</color>
    <color name="year">#888888</color>
    <color name="list_divider">#d9d9d9</color>
    <color name="list_row_start_color">#ffffff</color>
    <color name="list_row_end_color">#ffffff</color>
    <color name="list_row_hover_start_color">#ebeef0</color>
    <color name="list_row_hover_end_color">#ebeef0</color>

</resources>



5. Also add below dimensions in dimens.xml file located under res ⇒ values.

<resources>
        
    <dimen name="title">17dp</dimen>
    <dimen name="rating">15dip</dimen>
    <dimen name="genre">13dip</dimen>
    <dimen name="year">12dip</dimen>

</resources>



6. Before start writing java code, I would like to complete the UI part first. Create list_row_bg.xml, list_row_bg_hover.xml and list_row_selector.xml with below respective codes under res ⇒ drawable folder. If you don’t see drawable folder under res, create a new folder and name it as drawable.

list_row_bg.xml – Default style for list view row.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
  <gradient
      android:startColor="@color/list_row_start_color"
      android:endColor="@color/list_row_end_color"
      android:angle="270" />
</shape>

list_row_bg_hover.xml – Style for list view row when row is selected

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <gradient
        android:angle="270"
        android:endColor="@color/list_row_hover_end_color"
        android:startColor="@color/list_row_hover_start_color" />

</shape>

list_row_selector.xml – Selector file to toggle the row states.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/list_row_bg" android:state_pressed="false" android:state_selected="false"/>
    <item android:drawable="@drawable/list_row_bg_hover" android:state_pressed="true"/>
    <item android:drawable="@drawable/list_row_bg_hover" android:state_pressed="false" android:state_selected="true"/>

</selector>



7. Now open your layout file of main activity(in my case activity_main.xml) and add a ListView element.

<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" >

    <ListView
        android:id="@+id/list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:divider="@color/list_divider"
        android:dividerHeight="1dp"
        android:listSelector="@drawable/list_row_selector" />

</RelativeLayout>



8. We need to create another layout file for list view row. This is the main design component in this project as we define actual custom list design here. I am naming this file as list_row.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/list_row_selector"
    android:padding="8dp" >

    <!-- Thumbnail Image -->
    <com.android.volley.toolbox.NetworkImageView
        android:id="@+id/thumbnail"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_alignParentLeft="true"
        android:layout_marginRight="8dp" />

    <!-- Movie Title -->
    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@+id/thumbnail"
        android:layout_toRightOf="@+id/thumbnail"
        android:textSize="@dimen/title"
        android:textStyle="bold" />

    <!-- Rating -->
    <TextView
        android:id="@+id/rating"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/title"
        android:layout_marginTop="1dip"
        android:layout_toRightOf="@+id/thumbnail"
        android:textSize="@dimen/rating" />
    
    <!-- Genre -->
    <TextView
        android:id="@+id/genre"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/rating"
        android:layout_marginTop="5dp"
        android:layout_toRightOf="@+id/thumbnail"
        android:textColor="@color/genre"
        android:textSize="@dimen/genre" />

    <!-- Release Year -->
    <TextView
        android:id="@+id/releaseYear"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:textColor="@color/year"
        android:textSize="@dimen/year" />

</RelativeLayout>



Here we completed the design part. Let’s move to java part.

9. Create LruBitmapCache.java under util package. This class takes care of caching images on disk.

package info.androidhive.customlistviewvolley.util;

import com.android.volley.toolbox.ImageLoader.ImageCache;

import android.graphics.Bitmap;
import android.support.v4.util.LruCache;

public class LruBitmapCache extends LruCache<String, Bitmap> implements
		ImageCache {
	public static int getDefaultLruCacheSize() {
		final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
		final int cacheSize = maxMemory / 8;

		return cacheSize;
	}

	public LruBitmapCache() {
		this(getDefaultLruCacheSize());
	}

	public LruBitmapCache(int sizeInKiloBytes) {
		super(sizeInKiloBytes);
	}

	@Override
	protected int sizeOf(String key, Bitmap value) {
		return value.getRowBytes() * value.getHeight() / 1024;
	}

	@Override
	public Bitmap getBitmap(String url) {
		return get(url);
	}

	@Override
	public void putBitmap(String url, Bitmap bitmap) {
		put(url, bitmap);
	}
}



10. Create AppController.java under app package. This class is a singleton class which initializes core objects of volley library.

package info.androidhive.customlistviewvolley.app;

import info.androidhive.customlistviewvolley.util.LruBitmapCache;
import android.app.Application;
import android.text.TextUtils;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;

public class AppController extends Application {

	public static final String TAG = AppController.class.getSimpleName();

	private RequestQueue mRequestQueue;
	private ImageLoader mImageLoader;

	private static AppController mInstance;

	@Override
	public void onCreate() {
		super.onCreate();
		mInstance = this;
	}

	public static synchronized AppController getInstance() {
		return mInstance;
	}

	public RequestQueue getRequestQueue() {
		if (mRequestQueue == null) {
			mRequestQueue = Volley.newRequestQueue(getApplicationContext());
		}

		return mRequestQueue;
	}

	public ImageLoader getImageLoader() {
		getRequestQueue();
		if (mImageLoader == null) {
			mImageLoader = new ImageLoader(this.mRequestQueue,
					new LruBitmapCache());
		}
		return this.mImageLoader;
	}

	public <T> void addToRequestQueue(Request<T> req, String tag) {
		// set the default tag if tag is empty
		req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
		getRequestQueue().add(req);
	}

	public <T> void addToRequestQueue(Request<T> req) {
		req.setTag(TAG);
		getRequestQueue().add(req);
	}

	public void cancelPendingRequests(Object tag) {
		if (mRequestQueue != null) {
			mRequestQueue.cancelAll(tag);
		}
	}
}



11. Now add the AppController.java class in AndroidManifest.xml to your <application> tag using name property to execute this class on application start. Also add INTERNET permission as we are going to make network calls.

<application
        android:name="info.androidhive.customlistviewvolley.app.AppController" ../>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidhive.customlistviewvolley"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="11"
        android:targetSdkVersion="18" />

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

    <application
        android:name="info.androidhive.customlistviewvolley.app.AppController"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="info.androidhive.customlistviewvolley.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>



12. Now create Movie.java under model package. This model class will be used to provide movie objects data to list view after parsing the json.

package info.androidhive.customlistviewvolley.model;

import java.util.ArrayList;

public class Movie {
	private String title, thumbnailUrl;
	private int year;
	private double rating;
	private ArrayList<String> genre;

	public Movie() {
	}

	public Movie(String name, String thumbnailUrl, int year, double rating,
			ArrayList<String> genre) {
		this.title = name;
		this.thumbnailUrl = thumbnailUrl;
		this.year = year;
		this.rating = rating;
		this.genre = genre;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String name) {
		this.title = name;
	}

	public String getThumbnailUrl() {
		return thumbnailUrl;
	}

	public void setThumbnailUrl(String thumbnailUrl) {
		this.thumbnailUrl = thumbnailUrl;
	}

	public int getYear() {
		return year;
	}

	public void setYear(int year) {
		this.year = year;
	}

	public double getRating() {
		return rating;
	}

	public void setRating(double rating) {
		this.rating = rating;
	}

	public ArrayList<String> getGenre() {
		return genre;
	}

	public void setGenre(ArrayList<String> genre) {
		this.genre = genre;
	}

}



13. Create a class named CustomListAdapter.java with below code. This is a custom list adapter class which provides data to list view. In other words it renders the layout_row.xml in list by pre-filling appropriate information.

package info.androidhive.customlistviewvolley.adater;

import info.androidhive.customlistviewvolley.R;
import info.androidhive.customlistviewvolley.app.AppController;
import info.androidhive.customlistviewvolley.model.Movie;

import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.NetworkImageView;

public class CustomListAdapter extends BaseAdapter {
	private Activity activity;
	private LayoutInflater inflater;
	private List<Movie> movieItems;
	ImageLoader imageLoader = AppController.getInstance().getImageLoader();

	public CustomListAdapter(Activity activity, List<Movie> movieItems) {
		this.activity = activity;
		this.movieItems = movieItems;
	}

	@Override
	public int getCount() {
		return movieItems.size();
	}

	@Override
	public Object getItem(int location) {
		return movieItems.get(location);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {

		if (inflater == null)
			inflater = (LayoutInflater) activity
					.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		if (convertView == null)
			convertView = inflater.inflate(R.layout.list_row, null);

		if (imageLoader == null)
			imageLoader = AppController.getInstance().getImageLoader();
		NetworkImageView thumbNail = (NetworkImageView) convertView
				.findViewById(R.id.thumbnail);
		TextView title = (TextView) convertView.findViewById(R.id.title);
		TextView rating = (TextView) convertView.findViewById(R.id.rating);
		TextView genre = (TextView) convertView.findViewById(R.id.genre);
		TextView year = (TextView) convertView.findViewById(R.id.releaseYear);

		// getting movie data for the row
		Movie m = movieItems.get(position);

		// thumbnail image
		thumbNail.setImageUrl(m.getThumbnailUrl(), imageLoader);
		
		// title
		title.setText(m.getTitle());
		
		// rating
		rating.setText("Rating: " + String.valueOf(m.getRating()));
		
		// genre
		String genreStr = "";
		for (String str : m.getGenre()) {
			genreStr += str + ", ";
		}
		genreStr = genreStr.length() > 0 ? genreStr.substring(0,
				genreStr.length() - 2) : genreStr;
		genre.setText(genreStr);
		
		// release year
		year.setText(String.valueOf(m.getYear()));

		return convertView;
	}

}



14. Now open your main activity class (MainActivity.java) and do the below changes. Here I created volley’s JsonArrayRequest to get the json from url. Upon parsing the json, I stored all the json data into an ArrayList as Movie objects. Finally I called notifyDataSetChanged() on CustomListAdapter instance to render the list view with updated information.

package info.androidhive.customlistviewvolley;

import info.androidhive.customlistviewvolley.adater.CustomListAdapter;
import info.androidhive.customlistviewvolley.app.AppController;
import info.androidhive.customlistviewvolley.model.Movie;

import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.widget.ListView;

import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.VolleyLog;
import com.android.volley.toolbox.JsonArrayRequest;

public class MainActivity extends Activity {
	// Log tag
	private static final String TAG = MainActivity.class.getSimpleName();

	// Movies json url
	private static final String url = "http://api.androidhive.info/json/movies.json";
	private ProgressDialog pDialog;
	private List<Movie> movieList = new ArrayList<Movie>();
	private ListView listView;
	private CustomListAdapter adapter;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		listView = (ListView) findViewById(R.id.list);
		adapter = new CustomListAdapter(this, movieList);
		listView.setAdapter(adapter);

		pDialog = new ProgressDialog(this);
		// Showing progress dialog before making http request
		pDialog.setMessage("Loading...");
		pDialog.show();

		// changing action bar color
		getActionBar().setBackgroundDrawable(
				new ColorDrawable(Color.parseColor("#1b1b1b")));

		// Creating volley request obj
		JsonArrayRequest movieReq = new JsonArrayRequest(url,
				new Response.Listener<JSONArray>() {
					@Override
					public void onResponse(JSONArray response) {
						Log.d(TAG, response.toString());
						hidePDialog();

						// Parsing json
						for (int i = 0; i < response.length(); i++) {
							try {

								JSONObject obj = response.getJSONObject(i);
								Movie movie = new Movie();
								movie.setTitle(obj.getString("title"));
								movie.setThumbnailUrl(obj.getString("image"));
								movie.setRating(((Number) obj.get("rating"))
										.doubleValue());
								movie.setYear(obj.getInt("releaseYear"));

								// Genre is json array
								JSONArray genreArry = obj.getJSONArray("genre");
								ArrayList<String> genre = new ArrayList<String>();
								for (int j = 0; j < genreArry.length(); j++) {
									genre.add((String) genreArry.get(j));
								}
								movie.setGenre(genre);

								// adding movie to movies array
								movieList.add(movie);

							} catch (JSONException e) {
								e.printStackTrace();
							}

						}

						// notifying list adapter about data changes
						// so that it renders the list view with updated data
						adapter.notifyDataSetChanged();
					}
				}, new Response.ErrorListener() {
					@Override
					public void onErrorResponse(VolleyError error) {
						VolleyLog.d(TAG, "Error: " + error.getMessage());
						hidePDialog();

					}
				});

		// Adding request to request queue
		AppController.getInstance().addToRequestQueue(movieReq);
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		hidePDialog();
	}

	private void hidePDialog() {
		if (pDialog != null) {
			pDialog.dismiss();
			pDialog = null;
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}

Now run the project and test it once. Please do comment below if you ran into any issue.

]]>
http://www.androidhive.info/2014/07/android-custom-listview-with-image-and-text-using-volley/feed/ 0
Android Building Multi-Language Supported Apphttp://www.androidhive.info/2014/07/android-building-multi-language-supported-app/ http://www.androidhive.info/2014/07/android-building-multi-language-supported-app/#comments Mon, 21 Jul 2014 20:53:53 +0000 http://www.androidhive.info/?p=15146 Android is one of the few popular mobile operating systems having millions of users over 190 countries and growing day by day. So when you are aiming your app to be globally successful, it is always a good idea to make the app localized.

While localizing, you should consider using appropriate text, audio, currency, numbers and graphics depending upon the region or country. But this tutorial only covers localizing strings i.e supporting multiple languages. Localizing with Resources explains about other things should be considered when localizing your app.

Android Building Multi-Language Supported

In this article we are going to build a Multi-Language supported app that supports French, Deutsch (German), Hindi and Japanese.



1. How String Localization Works

By default android considers English as primary language and loads the string resources from res ⇒ values ⇒ strings.xml. When you want to add support for another language, you need to create a values folder by appending an Hyphen and the ISO language code. For example if you want to add support for French, you should create a values folder named values-fr and keep a strings.xml file in it with all the strings translated into French language.

In brief the localization works as follows

1. When user changes the device language through Settings ⇒ Language & Input, android OS itself checks for appropriate language resources in the app. (Let’s say user is selecting French)

2. If the app supports selected language, android looks for it’s string resources in values-(ISO language Code) folder in the project. (For french it loads the string values from values-fr/string.xml)

3. If the supported language strings.xml misses any string value, android always loads the missing strings from default strings.xml file i.e values/strings.xml

So it is mandatory that the default stings.xml file should contains all the string values that app uses. Other wise the app will crash with Force Close error.

Do’s:

While you are supporting multiple languages, you should consider below as a best practice while defining the strings. Always declare the string in strings.xml only.

<string name="note_email">Enter your email address</string>

When referring it in xml, use @strings notation.

<TextView ...   android:text="@string/note_email"  />

When defining the string through java code, use R.string

emailNote.setText(R.string.note_email);

Don’ts:

Never hard code the string in xml or in java code which make the translation difficult.

<TextView ...   android:text="Enter your email address"  />
emailNote.setText("Enter your email address");    



So let’s create a new project and try with an example.

2. Creating New Project

1. Create a new project in Eclipse by going to File ⇒ New ⇒ Android Application Project and fill the required information.

2. Add following colors in colors.xml located under values. If you don’t see colors.xml, create a new file and add the below colors.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="white">#ffffff</color>
    <color name="bg_gradient_start">#b21331</color>
    <color name="bg_gradient_end">#820d2a</color>
    <color name="bg_button_login">#380813</color>
</resources>

3. Under drawable folder create three files named bg_button_rounded.xml,bg_form_rounded.xml, bg_gradient.xml with following contents. These files are not related to language support, but just to give nice gradients background and rounded corners to buttons, input boxes.

bg_button_rounded.xml

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
 
    <!-- view background color -->
    <solid
        android:color="@color/bg_button_login" >
    </solid>
 
    <!-- If you want to add some padding -->
    <padding
        android:left="5dp"
        android:top="5dp"
        android:right="5dp"
        android:bottom="5dp"    >
    </padding>
 
    <!-- Here is the corner radius -->
    <corners
        android:radius="6dp"   >
    </corners>
 
</shape>

bg_form_rounded.xml

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
 
    <!-- view background color -->
    <solid
        android:color="@color/white" >
    </solid>
 
    <!-- If you want to add some padding -->
    <padding
        android:left="5dp"
        android:top="5dp"
        android:right="5dp"
        android:bottom="5dp"    >
    </padding>
 
    <!-- Here is the corner radius -->
    <corners
        android:radius="6dp"   >
    </corners>
 
</shape>

bg_gradient.xml

<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <gradient
        android:gradientRadius="750"
        android:endColor="@color/bg_gradient_end"
        android:startColor="@color/bg_gradient_start"
        android:type="radial" />
</shape>

4. Open strings.xml located under values folder and add following strings. These are default English language strings.

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">Multi Language App</string>
    <string name="action_settings">Settings</string>
    
    <string name="welcome">Welcome!</string>
    <string name="email">Email Address</string>
    <string name="password">Password</string>
    <string name="login">Login</string>
    <string name="signup">Don\'t have account? Sign Up</string>

</resources>

5. Now under res folder create three folders named values-de, values-fr, values-hi, values-ja and a strings.xml file in each of the folders.

Your project should look like this once you created the required files/folders.

android mult language support project structure

Now translate the strings into respected languages and place them in appropriate strings.xml files.

Deutsch values-de/strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    
    <string name="welcome">Willkommen!</string>
    <string name="email">Email Addresse</string>
    <string name="password">passowrd</string>
    <string name="login">Login</string>
    <string name="signup">müssen nicht angemeldet? Anmeldung</string>

</resources>



French values-fr/strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    
    <string name="welcome">accueil</string>
    <string name="email">adresse e-mail</string>
    <string name="password">mot de passe</string>
    <string name="login">connexion</string>
    <string name="signup">Ne pas avoir un compte? signer</string>

</resources>



Hindi values-hi/strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    
    <string name="welcome">स्वागतम</string>
    <string name="email">ईमेल पता</string>
    <string name="password">पासवर्ड</string>
    <string name="login">लॉगिन</string>
    <string name="signup">खाता नहीं है? साइन अप करें</string>

</resources>



Japanese values-ja/strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    
    <string name="welcome">歓迎</string>
    <string name="email">電子メールアドレス</string>
    <string name="password">パスワード</string>
    <string name="login">ログイン</string>
    <string name="signup">アカウントをお持ちでない場合は?サインアップ</string>

</resources>

6. Open your main activity layout file (in my case activity_main.xml) and add the following content to create a simple layout. This layout contains a title and a login form.

<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"
    android:background="@drawable/bg_gradient"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:gravity="center"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="40dp"
            android:text="@string/welcome"
            android:textColor="@color/white"
            android:textSize="45dp"
            android:textStyle="bold" />

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/bg_form_rounded"
            android:orientation="vertical" >

            <EditText
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="10dp"
                android:background="@null"
                android:hint="@string/email"
                android:padding="5dp" 
                android:singleLine="true"/>

            <EditText
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:background="@null"
                android:hint="@string/password"
                android:inputType="textPassword"           
                android:padding="5dp" />
        </LinearLayout>

        <Button
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="25dp"
            android:background="@drawable/bg_button_rounded"
            android:text="@string/login" 
            android:textColor="@color/white"/>
    </LinearLayout>
    
    <TextView android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/signup"
        android:layout_alignParentBottom="true"
        android:gravity="center_horizontal"
        android:layout_marginBottom="25dp"
        android:textColor="@color/white"/>

</RelativeLayout>

7. Open your MainActivity.java and make sure that it has following code. This code will be added automatically when you create new project.

package info.androidhive.multilanguageapp;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		getActionBar().hide();
	}
}

Now if you run the project you should see the app in English (assuming that your device is set to English language)

android-multi-language-app-english


3. Testing The Other Languages

In order to see the app in other languages follow below steps or check the above demo video.

1. On the device go to Settings ⇒ Language & Input
2. Select the Language and choose the language that you supported in the app.

android-multi-language-app-hindi
android-multi-language-app-deutsch
android-multi-language-app-french
android-multi-language-app-japanese


4. Android Localization Language ISO Codes

Below table give you ISO languages codes for all the languages that android supports.

LanguageLocalevalues/strings.xml
Germandevalues-de/strings.xml
Chinesezhvalues-zh/strings.xml
Czechcsvalues-cs/strings.xml
Dutchnlvalues-nl/strings.xml
Frenchfrvalues-fr/strings.xml
Italianitvalues-it/strings.xml
Japanesejavalues-ja/strings.xml
Koreankovalues-ko/strings.xml
Polishplvalues-pl/strings.xml
Russianruvalues-ru/strings.xml
Spanishesvalues-es/strings.xml
Arabicarvalues-ar/strings.xml
Bulgarianbgvalues-bg/strings.xml
Catalancavalues-ca/strings.xml
Croatianhrvalues-hr/strings.xml
Danishdavalues-da/strings.xml
Finnishfivalues-fi/strings.xml
Greekelvalues-el/strings.xml
Hebrewiwvalues-iw/strings.xml
Hindihivalues-hi/strings.xml
Hungarianhuvalues-hu/strings.xml
Indonesianinvalues-in/strings.xml
Latvianlvvalues-lv/strings.xml
Lithuanianltvalues-lt/strings.xml
Norwegiannbvalues-nb/strings.xml
Portugueseptvalues-pt/strings.xml
Romanianrovalues-ro/strings.xml
Serbiansrvalues-sr/strings.xml
Slovakskvalues-sk/strings.xml
Slovenianslvalues-sl/strings.xml
Swedishsvvalues-sv/strings.xml
Tagalogtlvalues-tl/strings.xml
Thaithvalues-th/strings.xml
Turkishtrvalues-tr/strings.xml
Ukrainianukvalues-uk/strings.xml
Vietnamesevivalues-vi/strings.xml

Source: http://bit.ly/1qYfHDL

5. Translation Services

Right now I used Google Translate service to translate the strings into other languages. But if you want more accurate and meaningful translation always go for professional services like Professional translations through Google Play

Finally Localization Checklist gives you list of things to be verified before the app goes live when localization considered.

]]>
http://www.androidhive.info/2014/07/android-building-multi-language-supported-app/feed/ 0
StealthGenie- An Android Monitoring App That Keep Things On Trackhttp://www.androidhive.info/2014/07/stealthgenie-an-android-monitoring-app-that-keep-things-on-track/ http://www.androidhive.info/2014/07/stealthgenie-an-android-monitoring-app-that-keep-things-on-track/#comments Sat, 19 Jul 2014 14:33:51 +0000 http://www.androidhive.info/?p=15446 You might have seen a lot of Android apps in the market that are providing employers instant business solutions and ease of communication. Similarly there are apps that are providing digital parenting solutions. Parents have also given Androids to their kids and believe that the android is helping them out in the educational hurdles. Well you are not wrong! But if you are a business employer who is heading an immense team of employees or a busy parent who is worried about your kids’ usage of mobile technology, the Android Spy App is going to be something imperative in your life.

You can’t think of monitoring your employees by hiring a private investigator or sneaking your kids physically. In this era of digitalization you have to think about spy app. When it comes to the spy app, you will see multiple apps in the market. What is your first priority while choosing any spy app? Obviously, the one that is undetectable. You can think of StealthGenie that is not only undetectable, but also provides instant monitoring solutions to its users.

This is how one parent is using StealthGenie and monitoring his kid who has been studying in a boarding school.

Here is what employers have to say about StealthGenie that is helping them in running their delivery business smoothly.

So, here are the 9 exclusive features that compel one to go for StealthGenie. These features outweigh the use of mobile technology by creating a balance in your life.

1. Check Call Logs

Won’t you get concerned when your kid hesitates while talking in front of you? It becomes necessary for you to check the call details. With this feature you can keep a check on what numbers are calling or what is the frequency of calls by particular number. You can also see the time and date of the phone call.

2. Monitor Chats On Instant Messengers

Do you know sexting has gone mainstream? The sexting scandals are on the rise. According to teenage sexting statistics, 71% of teen girls and 67% of teen boys who have sent or posted sexually suggestive content say they have sent/posted this content to a boyfriend/girlfriend. This is quite alarming. Similarly, you never know if your employee is sending messages that can put your market reputation at risk. You can monitor WhatsApp chats, Viber chats, Skype chats or even Facebook messages with this feature. This can help you out in formulating limitations guidelines accordingly.

3. Record The Calls

Why your kid is looking worried after the phone call? What if your employee is talking to your competitor? If you want to know more, put their calls on the recording with the call recording feature. You can even put specific numbers on call recording and can listen to these recordings later.

4. Put A Digital Fence

Geo tracking is the common feature that it offers and can tell you the exact location of the mobile, but Geo fencing is like your digital guard that will notify you if your kid enters into the particular area. All you need to do is mark the particular areas in red and green zones. You can also create a fence in more than one location simultaneously. You just have to choose the preference like how you want to be notified. You can get the alerts via SMS, via emails or even both. The choice is completely up to you.

5. Read Incoming/Outgoing Emails

You can read all kinds of business communication that your employee is making with clients and with your partner companies. Likewise you will get to know if your kids are receiving spamming emails.

6. Look At Multimedia

You can get access to photos, videos or kind of music your kids have saved on their mobile. Moreover, you can check the employee phone, if it has any confidential information on their mobile in the form of photos or recordings of any business meetings.

7. Monitor Internet Activities

With this feature you can monitor what kind of websites your kids and employees are visiting. You can check that whether the visited websites are age appropriate for your kids or not. You can even look at the bookmarked websites.

8. Get Instant Alerts

Apart from Geo-fencing feature, if you don’t want your kid to use particular words in their emails and messages, you can put them in a list of suspicious words. If your kid will use these words while texting or in emails you will be notified via SMS and email about its use. Moreover, you can also get alerts if a particular number is calling.

9. Remote Control The Phone

This feature works amazingly. If your kid’s phone is lost or your employee phone is lost what you are most concerned about? Obviously it’s the multimedia, the confidential business data and contact list. The phone may also have bank account information. With remote control feature you can send command to the phone so that it can get locked. You can also send remote command in order to wipe out all phone data. You know the best part? The data will remain in the online control panel. Means you don’t need to worry about data backup if you want.

9

Exclusivity+Affordability- What Else Could Be Better?

I am sure you are going to install it now! Let me surprise you again by telling that it is conveniently affordable. Packages start from as low as $8 per month. You can hardly say ‘NO’ to this exclusive affordable package.

Now the Smartphone users can get amazing monitoring experience with StealthGenie Spy App. Remember, it’s never too late to take a good decision for your business and family.

]]>
http://www.androidhive.info/2014/07/stealthgenie-an-android-monitoring-app-that-keep-things-on-track/feed/ 0