Showing posts with label android. Show all posts
Showing posts with label android. Show all posts

12 September, 2014

Sharing a local photo to Facebook on Android using Facebook SDK

Most of the steps depicted here can be found on Facebook Developers page but sharing a photo while showing the share dialog isn't fully documented.

Download SDK

Download the Facebook SDK and extract it.

Setup Workspace

Add the Facebook SDK project to your workspace and link the library to your project.

Register App on Facebook

Follow these steps to create a Facebook app and get the Application Id.

Android Manifest

Add these elements to your app Manifest file inside the application element and replace the 123456789456789 of the provider with the application id you got from the facebook page.
<activity android:name="com.facebook.LoginActivity" android:label="@string/app_name" android:theme="@android:style/Theme.Translucent.NoTitleBar" />

<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/facebook_app_id"/>

<provider android:authorities="com.facebook.app.NativeAppCallContentProvider123456789456789" android:name="com.facebook.NativeAppCallContentProvider" android:exported="true" />
The provider was what I was missing from my solution resulting on the photo being published without showing any dialog to the user.
Don't forget to add the facebook_app_id string in your string.xml file.

Initialization

I ended up using Android Simple Facebook library to login the user if necessary since it's really easy to use. 
private Permission[] permissions = new Permission[] { Permission.PUBLISH_ACTION };

public initialization(String appId, String appNamespace, Bundle savedInstanceState) {
 // Facebook SDK initialization
 uiHelper = new UiLifecycleHelper(this, null /*statusCallback*/);
 uiHelper.onCreate(savedInstanceState);

 // Simple Facebook Library initialization
 SimpleFacebookConfiguration configuration = new SimpleFacebookConfiguration.Builder().setAppId(appId).setNamespace(appNamespace).setPermissions(permissions).build();
 SimpleFacebook.setConfiguration(configuration);
}
Use the status callback to get Facebook session status
It is also needed to attach some lifecycle events to these libraries.
@Override
protected void onSaveInstanceState(Bundle outState) {
 super.onSaveInstanceState(outState);
 uiHelper.onSaveInstanceState(outState);
}

@Override
protected void onResume() {
 super.onResume();
 uiHelper.onResume();
 mSimpleFacebook = SimpleFacebook.getInstance(activity);
}

@Override
protected void onPause() {
 super.onPause();
 uiHelper.onPause();
}

@Override
protected void onDestroy() {
 uiHelper.onDestroy();
 super.onDestroy();
}

Login user

Before actually posting the user is requested to be logged in and authorize the app. Since we're using the Facebook SDK it requires the Facebook App to be installed on the device. Since the user will already be logged in the Facebook App this post will not request the user to login again. The first post will, however, request permission for your app to post in the user timeline.
As stated above, I've used Simple Facebook library for login.
public void checkFacebookLogin(final File photo) {
 if (facebook.isLoggedIn()) {
  postPhoto(photo);
 } else {
  facebook.logIn(new OnLoginListener() {

   @Override
   public void onFail(String result) {}

   @Override
   public void onException(Throwable exception) {}

   @Override
   public void onThinking() {}

   @Override
   public void onNotAcceptingPermissions(Type type) {}

   @Override
   public void onLogin() {
    postPhoto(photo);
   }
  });
 }
}
On production code you should actually handle the other events.

Post Photo

Actually post the photo.
public void postPhoto(File photo) {
 if (FacebookDialog.canPresentShareDialog(this, FacebookDialog.ShareDialogFeature.PHOTOS)) {
  FacebookDialog shareDialog = new FacebookDialog.PhotoShareDialogBuilder(this).addPhotoFiles(Arrays.asList(photos)).build();
  uiHelper.trackPendingDialogCall(shareDialog.present());
 }
}
Since the WebDialog provided by the Facebook SDK doesn't post photos it looks like we're stuck with this method.

19 June, 2013

Custom fonts in Android (Part 3 of 3) - Memory leaks and FontsHelper

As reported here in issue 9904 there is a memory leak if we call Typeface.createFromAsset() multiple times as the fonts are kept open.
To avoid this problem and to keep from accessing assstes multiple times we'll create FontsHelper, a class that will contain all typefaces needed by the app.

public class FontsHelper {

 public static final String ROBOTO_LIGHT = "fonts/Roboto-Light.ttf";
 public static final String ROBOTO_BOLD = "fonts/Roboto-Bold.ttf";
 public static final String ROBOTO_CONDENSED = "fonts/Roboto-Condensed.ttf";
 
 private static Map fonts = new HashMap();
 
 public static Typeface getTypeFace(Context context, String fontPath) {
  
  if (!fonts.containsKey(fontPath)) {
   
   Typeface font = Typeface.createFromAsset(context.getAssets(), fontPath);
   fonts.put(fontPath, font);
  }
  
  return fonts.get(fontPath);
 }
 
 public static void setFont(View view, Typeface font) {
 
        if (view instanceof ViewGroup) 
  {
            for (int i = 0; i < ((ViewGroup)view).getChildCount(); i++) {
   
                setFont(((ViewGroup)view).getChildAt(i), font);
            }
        } else if (view instanceof TextView) {
  
            ((TextView) view).setTypeface(font);
        }
    }
 
 public static void setFont(Context ctx, TextView view, AttributeSet attrs) {

  TypedArray styleAttrs = context.obtainStyledAttributes(attrs, R.styleable.CustomFontView);
  String customFont = styleAttrs.getString(R.styleable.CustomFontView_customFont);
  setFont(context, view, customFont);
  styleAttrs.recycle();
 }

 public static boolean setFont(Context ctx, TextView view, String fontPath) {

  boolean successful = true;
 
  try {
  
   Typeface tf = AssetsHelper.getTypeFace(ctx, fontPath);
   view.setTypeface(tf);
  } catch (Exception e) {
   
   Log.e(TAG, "Error to get typeface: " + e.getMessage());
   successful = false;
  }

  return successful;
 }
This way we avoid the memory leaks and can further simplify the CustomFontTextView class from the previous post.

public class CustomFontTextView extends TextView {

 public CustomFontTextView(Context context, AttributeSet attrs) {

  super(context, attrs);
  FontsHelper.setFont(context, this, attrs);
 }

 public CustomFontTextView(Context context, AttributeSet attrs, int defStyle) {

  super(context, attrs, defStyle);
  FontsHelper.setFont(context, this, attrs);
 }
 
 public CustomFontTextView(Context context, String fontPath) {
  
  super(context);
  FontsHelper.setFont(context, this, fontPath);
 }
}
With the custom font static variables in FontsHelper and the constructor with a fontPath argument in CustomFontTextView we can also apply the font by code in an easier way.

Custom fonts in Android (Part 2 of 3) - Setting font in xml

The objective is to have the following xml:

<?xml version="1.0" encoding="utf-8"?>
<com.demo.LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:control="http://schemas.android.com/apk/res/com.demo"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.demo.CustomFontTextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        control:customFont="fonts/Roboto-Light.ttf" />

</com.demo.LinearLayout>

To enable this we need to declare a custom attribute in values\attrs.xml.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    
    <declare-styleable name="CustomFontView">
        <attr name="customFont" format="string" />
    </declare-styleable>

</resources>
Now we can add the control namespace as seen in the first snippet in line 4.
To read the customFont value and actually apply it to the TextView we're going to extend it and read the new attribute. The CustomFontTextView could be something like this:

public class CustomFontTextView extends TextView {

 public CustomFontTextView(Context context, AttributeSet attrs) {

  super(context, attrs);
  setCustomFont(context, this, attrs);
 }
 
 public void setCustomFont(Context context, TextView view, AttributeSet attrs) {

  TypedArray styleAttrs = context.obtainStyledAttributes(attrs, R.styleable.CustomFontView);
  String customFont = styleAttrs.getString(R.styleable.CustomFontView_customFont);
  setCustomFont(context, view, customFont);
  styleAttrs.recycle();
 }
}
Just like this CustomFontTextView, the same method can be applied to all views. To apply to multiple views inside a complex view check Arnaud's answer in http://stackoverflow.com/questions/9797872/use-roboto-font-for-earlier-deviceshttp://stackoverflow.com/questions/9797872/use-roboto-font-for-earlier-devices.

To avoid having the same path copied all over your xml files you may consider creating styles and put the path there.

<style name="RobotoTextView" parent="android:Widget.Holo.Light.TextView">
 <item name="customFont">fonts/Roboto-Bold.ttf</item>
</style>
And then applying that style in your custom controls:
    <com.demo.CustomFontTextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        style="@style/RobotoTextView" />
On the last part we will focus on a memory leak you may find and how to avoid it.

Custom fonts in Android (Part 1 of 3) - Adding to project and set by code

To use custom fonts in Android add the necessary font files to assets/fonts (you may need to create the assets folder).
To apply them by code use the following snippet:

Typeface font = Typeface.createFromAsset(getAssets(), "fonts/Roboto-Light.ttf");
textView.setTypeface(font);
On the second part of this series we'll apply custom fonts in xml.
UPDATE: Oh, and please don't include unnecessary fonts in the asstes folder as seen above as it will bloat your apk. Thanks.