Hi to all in previous two articles we learnt how to integrate Twitter authentication in our app and show different kind of Twitter Timelines in our app. Today we will learn a little bit more about Twitter APIs i.e Composing Tweets from our App and sharing Image and Text using Twitter Compose API.
2. Alert Dialog.
4. Picking Image from Gallery.
5. Capture Image using Camera.
Please go through above given prerequisite link so that you will better understand the article.
TweetComposer basically provides two ways to compose Tweets:
1. Launch the Twitter application’s Tweet Composer ; A feature-rich composer which supports attaching images and videos.
2. Launch the Twitter Kit Native Composer : A lightweight composer which lets users compose Tweets from within your application.
To compose Tweet using this way use the below simple code:
1 2 3 4 |
TweetComposer.Builder builder = new TweetComposer.Builder(this) .text("Any text here.")//any sharing text here .image(imageUri);//sharing image uri builder.show(); |
The image Uri should be a Uri using the content:// scheme. For example,
1 2 3 |
Uri imageUri = FileProvider.getUriForFile(MainActivity.this, "com.package_name" + ".file_provider", new File("/path/to/image")); |
If the Twitter app is not installed, the intent will launch twitter.com in a browser, but the specified image will be ignored. For more details on correctly setting up a FileProvider see Setup Sharing.
For devices who doesn’t have Twitter App installed then the browser will open like below with pre-fill text you passed.
To compose Tweet using this way use the below sample code:
1 2 3 4 5 6 7 8 9 |
final TwitterSession session = TwitterCore.getInstance().getSessionManager() .getActiveSession();//get authenticated user twitter session final Intent intent = new ComposerActivity.Builder(YourActivity.this) .session(session)//pass the authenticated user session .image(uri)//image uri to share .text("Text to share")//any text to be shared .hashtags("#twitter")//hash tag .createIntent(); startActivity(intent); |
After attempting to post a Tweet, the TweetUploadService broadcasts an Intent with the action values:
You can create a BroadcastReceiver to receive these Intents.
1 2 3 4 5 6 7 8 9 10 11 12 |
@Override public void onReceive(Context context, Intent intent) { if (TweetUploadService.UPLOAD_SUCCESS.equals(intent.getAction())) { // success final Long tweetId = intentExtras.getLong(TweetUploadService.EXTRA_TWEET_ID); } else if (TweetUploadService.UPLOAD_FAILURE.equals(intent.getAction())) { // failure final Intent retryIntent = intentExtras.getParcelable(TweetUploadService.EXTRA_RETRY_INTENT); } else if (TweetUploadService.TWEET_COMPOSE_CANCEL.equals(intent.getAction())) { // cancel } } |
Don’t forget to add your BroadcastReceiver to the application AndroidManifest.xml.
1 2 3 4 5 6 7 8 9 |
<receiver android:name=".BroadcastReceiverName" android:exported="false"> <intent-filter> <action android:name="com.twitter.sdk.android.tweetcomposer.UPLOAD_SUCCESS"/> <action android:name="com.twitter.sdk.android.tweetcomposer.UPLOAD_FAILURE"/> <action android:name="com.twitter.sdk.android.tweetcomposer.TWEET_COMPOSE_CANCEL"/> </intent-filter> </receiver> |
On successful tweet post you will be able to see that Tweet inside your Tweet timeline.
1. Create an App on Twitter and get API Key and Secret. To know how to get these things check my previous tutorial.
2. After getting API Key and Secret open build.gradle and add the following dependencies to it and sync the gradle.
1 2 3 4 5 6 7 |
//Twitter Core API dependency compile 'com.twitter.sdk.android:twitter-core:3.1.1' //Twitter Compose API dependency required to Share images compile 'com.twitter.sdk.android:tweet-composer:3.1.1' //Picasso dependency to load picked/captured images implementation 'com.squareup.picasso:picasso:2.5.2' |
3. On syncing completion create an Application java class naming MyApplication.java where we will initialise Twitter.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package com.compose_tweets_demo.main; import android.app.Application; import android.util.Log; import com.twitter.sdk.android.core.DefaultLogger; import com.twitter.sdk.android.core.Twitter; import com.twitter.sdk.android.core.TwitterAuthConfig; import com.twitter.sdk.android.core.TwitterConfig; /** * Created by sonu on 19/01/18. */ public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); //initialise twitter config TwitterConfig config = new TwitterConfig.Builder(this) .logger(new DefaultLogger(Log.DEBUG)) .twitterAuthConfig(new TwitterAuthConfig("API_KEY", "API_SECRET"))//replace your twitter API Key and Secret here .debug(true) .build(); Twitter.initialize(config); } } |
4. Now declare create Application in AndroidManifest.xml.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<application android:name=".main.MyApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".main.MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> |
5. Lets create an xml layout naming activity_main.xml and place the following code into it. In this class i have taken two Button to show both types of Compose Tweets and an ImageView to select and show picked/captured Image.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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:orientation="vertical" tools:context="com.compose_tweets_demo.main.MainActivity"> <!-- Image view to show picked/captured image --> <ImageView android:id="@+id/picked_image_view" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" android:background="#EEEEEE" android:onClick="triggerPickImageTask" android:padding="10dp" android:scaleType="centerCrop" android:src="@drawable/ic_add_a_photo_black_24dp" /> <!-- Buttons to share image via Twitter Composer and Twitter Native Composer --> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="2" android:gravity="center" android:orientation="vertical" android:padding="16dp"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorPrimary" android:onClick="shareUsingTwitterComposer" android:text="@string/share_using_twitter_composer" android:textColor="@android:color/white" android:textSize="15sp" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:background="@color/colorPrimary" android:onClick="shareUsingTwitterNativeComposer" android:text="@string/share_using_tweeter_native_composer" android:textColor="@android:color/white" android:textSize="15sp" /> </LinearLayout> </LinearLayout> |
6. Now we will see how to pick image from Gallery and capture image from Camera. For this we will be using FileProvider.
FileProvider is a special subclass of ContentProvider that facilitates secure sharing of files associated with an app by creating a content:// Uri for a file instead of a file:/// Uri.
The increased level of file access security offered by a content URI makes FileProvider a key part of Android’s security infrastructure.
To do this define FileProvider in your manifest file:
1 2 3 4 5 6 7 8 9 10 11 |
<!-- File Provider for Storing images mandatory. Here authorities should match with the same that we use while Capturing image--> <!-- For more details check this link :https://developer.android.com/reference/android/support/v4/content/FileProvider.html --> <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.example.file_provider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" /> </provider> |
Set the android:authorities attribute to a URI authority based on a package name of your project.For example, if you control the package name com.example you should use the authority com.example.file_provider. Set the android:exported attribute to false; the FileProvider does not need to be public. Set the android:grantUriPermissions attribute to true, to allow you to grant temporary access to files.
A FileProvider can only generate a content URI for files in directories that you specify beforehand. To specify a directory, specify the its storage area and path in XML, using child elements of the <paths> element. For example, the following paths element tells FileProvider that you intend to request content URIs for the images/ subdirectory of your private file area.
1 2 3 4 |
<paths xmlns:android="http://schemas.android.com/apk/res/android"> <files-path name="my_images" path="images/"/> ... </paths> |
There are lot more options to create and add your provider paths that you can check here.
7. Now open MainActivity.java and add the below code to it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 |
package com.compose_tweets_demo.main; import android.Manifest; import android.app.Activity; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.v4.content.FileProvider; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.ImageView; import android.widget.Toast; import com.compose_tweets_demo.R; import com.compose_tweets_demo.utils.CameraUtils; import com.compose_tweets_demo.utils.FileNameCreation; import com.compose_tweets_demo.utils.MarshMallowPermission; import com.squareup.picasso.Picasso; import com.twitter.sdk.android.core.Callback; import com.twitter.sdk.android.core.Result; import com.twitter.sdk.android.core.TwitterCore; import com.twitter.sdk.android.core.TwitterException; import com.twitter.sdk.android.core.TwitterSession; import com.twitter.sdk.android.core.identity.TwitterAuthClient; import com.twitter.sdk.android.tweetcomposer.ComposerActivity; import com.twitter.sdk.android.tweetcomposer.TweetComposer; import java.io.File; public class MainActivity extends AppCompatActivity { private ImageView pickedImageView; /** * REQUEST CODE for taking permission and picking/capturing image */ private static final int GALLERY_REQUEST_CODE = 332; private static final int CAMERA_REQUEST_CODE = 333; private static final int SHARE_PERMISSION_CODE = 223; //URI of picked/captured image private Uri cameraFileURI; //Twitter auth client to do custom Twitter login private TwitterAuthClient client; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //find id of ImageView pickedImageView = findViewById(R.id.picked_image_view); } /** * method called when user click on pickImageView * * @param view of the calling element */ public void triggerPickImageTask(View view) { checkStorageAndCameraPermission(); } /** * check if the app has the CAMERA and STORAGE permission to perform the operation * This method will automatically ask permission to user if permission is not granted. */ private void checkStorageAndCameraPermission() { if (MarshMallowPermission.checkMashMallowPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, SHARE_PERMISSION_CODE)) { onPermissionGranted(); } } /** * method called when user want to share image and text using Twitter Composer * NOTE : If the Twitter app is not installed, the intent will launch twitter.com in a browser, * but the specified image will be ignored. * * @param view of the calling element */ public void shareUsingTwitterComposer(View view) { //check if user has picked/captured image or not if (cameraFileURI != null) { TweetComposer.Builder builder = new TweetComposer.Builder(this) .text("This is a testing tweet!!")//pass any tweet message here .image(cameraFileURI);//pass captured/picked image URI builder.show(); } else { //if not then show dialog to pick/capture image Toast.makeText(this, "Please select image first to share.", Toast.LENGTH_SHORT).show(); checkStorageAndCameraPermission(); } } /** * method to share picked/capture image with text using Twitter Native Composer * NOTE : For this you should authenticate user before sharing image as the builder required TwitterSession. * It does not depend on the Twitter for Android app being installed. * * @param view */ public void shareUsingTwitterNativeComposer(View view) { //check if user has picked/captured image or not if (cameraFileURI != null) { TwitterSession session = TwitterCore.getInstance().getSessionManager() .getActiveSession();//get the active session if (session != null) { //if active session is not null start sharing image shareUsingNativeComposer(session); } else { //if there is no active session then ask user to authenticate authenticateUser(); } } else { //if not then show dialog to pick/capture image Toast.makeText(this, "Please select image first to share.", Toast.LENGTH_SHORT).show(); checkStorageAndCameraPermission(); } } /** * method to share image using Twitter Native Kit composer * * @param session of the authenticated user */ private void shareUsingNativeComposer(TwitterSession session) { Intent intent = new ComposerActivity.Builder(this) .session(session)//Set the TwitterSession of the User to Tweet .image(cameraFileURI)//Attach an image to the Tweet .text("This is Native Kit Composer Tweet!!")//Text to prefill in composer .hashtags("#android")//Hashtags to prefill in composer .createIntent();//finally create intent startActivity(intent); } /** * method call to authenticate user */ private void authenticateUser() { client = new TwitterAuthClient();//init twitter auth client client.authorize(this, new Callback<TwitterSession>() { @Override public void success(Result<TwitterSession> twitterSessionResult) { //if user is successfully authorized start sharing image Toast.makeText(MainActivity.this, "Login successful.", Toast.LENGTH_SHORT).show(); shareUsingNativeComposer(twitterSessionResult.data); } @Override public void failure(TwitterException e) { //if user failed to authorize then show toast Toast.makeText(MainActivity.this, "Failed to authenticate by Twitter. Please try again.", Toast.LENGTH_SHORT).show(); } }); } /** * when permission is granted for CAMERA and STORAGE show alert dialog with two options : CAMERA and GALLERY */ private void onPermissionGranted() { new AlertDialog.Builder(this) .setTitle("Select Option") .setItems(new String[]{"Gallery", "Camera"}, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { switch (i) { case 0: //Gallery selectImageFromGallery(); break; case 1: //Camera captureImageFormCamera(); break; } } }) .setCancelable(true) .create() .show(); } /** * start activity to pick image from gallery */ private void selectImageFromGallery() { Intent in = new Intent(Intent.ACTION_PICK); in.setType("image/*"); startActivityForResult(in, GALLERY_REQUEST_CODE); } /** * start activity to capture image */ private void captureImageFormCamera() { //check if device support camera or not if not then don't do anything if (!CameraUtils.isDeviceSupportCamera(this)) { return; } Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); //Get the file URI using the below code //here in place of AUTHORITY you have to pass <package_name>.file_provider //NOTE :For more details check this link :https://developer.android.com/reference/android/support/v4/content/FileProvider.html cameraFileURI = FileProvider.getUriForFile(this, "com.compose_tweets_demo.file_provider", FileNameCreation.createImageFile(this)); //after getting image URI pass it via Intent intent.putExtra(MediaStore.EXTRA_OUTPUT, cameraFileURI); //grant URI permission to access the create image URI for (ResolveInfo resolveInfo : getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)) { grantUriPermission(resolveInfo.activityInfo.packageName, cameraFileURI, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); } //here check if there ia any app available to perform camera task or not if not then show toast //NOTE : This condition is not required because every device has Camera app but in rare cases some device don't have camera //so to avoid that thing we have to add this condition if (intent.resolveActivity(getPackageManager()) != null) { startActivityForResult(intent, CAMERA_REQUEST_CODE); } else { Toast.makeText(this, "No apps to capture image.", Toast.LENGTH_SHORT).show(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { //check if all multiple permissions are granted or not case SHARE_PERMISSION_CODE: if (permissions.length > 0 && grantResults.length > 0) { int counter = 0; for (int result : grantResults) { if (result != 0) { onPermissionDenied(); return; } counter++; } if (counter == permissions.length) { //All permission granted onPermissionGranted(); } } break; } } /** * if any one of the permission is denied show a dialog to user to grant permission again if they want */ private void onPermissionDenied() { new AlertDialog.Builder(this) .setMessage("Both permission are required to pick/capture image. Do you want to try again.") .setPositiveButton("Ok", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { //if user click ok then again ask for permission checkStorageAndCameraPermission(); } }) .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { //if user click on cancel show toast Toast.makeText(MainActivity.this, "You cannot share the images without giving these permissions.", Toast.LENGTH_SHORT).show(); } }) .setCancelable(false) .create() .show(); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case GALLERY_REQUEST_CODE: if (resultCode == Activity.RESULT_OK) { //get the picked image URI Uri imageUri = data.getData(); //set the picked image URI to created variable this.cameraFileURI = imageUri; //now display picked image over ImageView displayImage(imageUri); } else { //if user cancelled or failed to pick image show toast Toast.makeText(this, "Failed to pick up image from gallery.", Toast.LENGTH_SHORT).show(); } break; case CAMERA_REQUEST_CODE: if (resultCode == Activity.RESULT_OK) { //check if Camera URI is null or not if (cameraFileURI != null) { //if not null then show URI over image view displayImage(cameraFileURI); } else { //if URI is null show toast Toast.makeText(this, "Failed to capture image.", Toast.LENGTH_SHORT).show(); } } else { //if user cancelled capture image then show toast Toast.makeText(this, "Failed to capture image.", Toast.LENGTH_SHORT).show(); } break; default: //put this here as Twitter requires to send result back to our Class if (client != null) client.onActivityResult(requestCode, resultCode, data); break; } } /** * method to show URI over image uri * NOTE : I am using picasso to load images as picasso will automatically scale large size images * and display very efficiently over ImageView * * @param imageUri of the picked/captured image */ private void displayImage(Uri imageUri) { Picasso.with(this).load(imageUri).into(pickedImageView); } } |
In this class the below code is responsible to show how to get URI using FileProvider:
1 2 3 4 5 6 |
Uri cameraFileURI = FileProvider.getUriForFile(this, "com.compose_tweets_demo.file_provider", FileNameCreation.createImageFile(this)); //grant URI permission to access the create image URI for (ResolveInfo resolveInfo : getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)) { grantUriPermission(resolveInfo.activityInfo.packageName, cameraFileURI, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); } |
8. After adding code to MainActivity.java there are some Utils java class that you should be needing.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
package com.compose_tweets_demo.utils; import android.content.Context; import android.content.pm.PackageManager; import android.widget.Toast; /** * Created by sonu on 19/01/18. */ public class CameraUtils { /** * method to check if Device support camera or not * @param context of calling class * @return if camera is supportable or not */ public static boolean isDeviceSupportCamera(Context context) { if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) { return true; } Toast.makeText(context, "Device doesn't support camera.", Toast.LENGTH_SHORT).show(); return false; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
package com.compose_tweets_demo.utils; import android.content.Context; import android.os.Environment; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; public class FileNameCreation { /** * method to return File of created image * @param context of calling class * @return File of the created image */ public static File createImageFile(Context context) { // Create an image file name String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()); String imageFileName = "JPEG_" + timeStamp + "_"; //create image file name under Pictures directory File storageDir = new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), "TweetImages"); //if directory doesn't exist create directory if (!storageDir.exists()) { storageDir.mkdir(); } File image = null; try { //now create jpg image under created directory image = File.createTempFile( imageFileName, /* prefix */ ".jpg", /* suffix */ storageDir /* directory */ ); } catch (IOException e) { e.printStackTrace(); } return image; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
package com.compose_tweets_demo.utils; import android.content.Context; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import java.util.ArrayList; public class MarshMallowPermission { /** * method check if permission is granted or not if not then it will show dialog * @param context of the calling class * @param permission to check if granted or not * @return */ public static int checkPermission(Context context, String permission) { return ContextCompat.checkSelfPermission(context, permission); } /** * method to check if all multiple/single permission is granted or not * @param appCompatActivity calling activity * @param permissionsToGrant permission list in string array * @param PERMISSION_REQUEST_CODE to identify every permission check * @return if granted or not */ public static boolean checkMashMallowPermissions(AppCompatActivity appCompatActivity, String[] permissionsToGrant, int PERMISSION_REQUEST_CODE) { ArrayList<String> permissions = new ArrayList<>(); for (String permission : permissionsToGrant) { if (checkPermission(appCompatActivity, permission) != 0) { permissions.add(permission); } } //if all permission is granted if (permissions.size() == 0) { return true; } ActivityCompat.requestPermissions(appCompatActivity, permissions.toArray(new String[permissions.size()]), PERMISSION_REQUEST_CODE); return false; } } |
9. Don’t forget to add below mentioned permissions to AndroidManifest.xml.
1 2 3 4 5 6 7 |
<!-- permission required to share images --> <uses-permission android:name="android.permission.INTERNET" /> <!-- permission required to Capture and Pick image --> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> |
Your AndroidManifest will look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.compose_tweets_demo"> <!-- permission required to share images --> <uses-permission android:name="android.permission.INTERNET" /> <!-- permission required to Capture and Pick image --> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application android:name=".main.MyApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".main.MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!-- File Provider for Storing images mandatory. Here authorities should match with the same that we use while Capturing image--> <!-- For more details check this link :https://developer.android.com/reference/android/support/v4/content/FileProvider.html --> <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.compose_tweets_demo.file_provider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" /> </provider> <!-- Broadcast Receiver only for Native Twitter Composer to listen event --> <receiver android:name=".main.MyTweetResultReceiver" android:exported="false"> <intent-filter> <action android:name="com.twitter.sdk.android.tweetcomposer.UPLOAD_SUCCESS" /> <action android:name="com.twitter.sdk.android.tweetcomposer.UPLOAD_FAILURE" /> <action android:name="com.twitter.sdk.android.tweetcomposer.TWEET_COMPOSE_CANCEL" /> </intent-filter> </receiver> </application> </manifest> |
10. For FileProvider xml path use the below code:
1 2 3 4 5 6 7 8 9 10 |
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <!-- here name is : A URI path segment. To enforce security, this value hides the name of the subdirectory you're sharing. The subdirectory name for this value is contained in the path attribute. --> <!-- path is : The subdirectory you're sharing. While the name attribute is a URI path segment, the path value is an actual subdirectory name. Notice that the value refers to a subdirectory, not an individual file or files. You can't share a single file by its file name, nor can you specify a subset of files using wildcards.--> <external-path name="tweet_images" path="Android/data/com.compose_tweets_demo/files/Pictures/TweetImages/" /> <!-- For more details check this link :https://developer.android.com/reference/android/support/v4/content/FileProvider.html --> </paths> |
11. Finally all done , now you can also create app to Compose Tweets.
Thanks.
NOTE : After downloading don’t forget to place your API Key and Secret inside MyApplication.java.
Subscribe to us and get the latest news.
15 Comments
shakir khan
Wednesday, December 26th, 2018This is the best article , solve my all issues..Thank you for the article
Ramashish Prajapati
Thursday, January 3rd, 2019How to send video with hashtag in twitter like image your sending??
Dr. Droid
Friday, January 4th, 2019Hi Ramashish,
I had done research about how to share video with hashtag and i don’t think so its possible. Please go through the below link:
https://stackoverflow.com/questions/46659942/how-to-share-video-in-twitter-using-twitter-kit-3-in-the-android
Thanks
Dharmesh Patel
Monday, February 11th, 2019I have used this code to compose tweet but getting TweitterSession error. Here, I attached log, please help me to resolved this issue.
final TwitterSession session = TwitterCore.getInstance().getSessionManager().getActiveSession();
final Intent intent = new ComposerActivity.Builder(MainActivity.this)
.session(session)
.text(“Love where you work”)
.hashtags(“#twitter”)
.createIntent();
startActivity(intent);
Logs::
TwitterSession must not be null
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2450)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2510)
at android.app.ActivityThread.-wrap11(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1363)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5461)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.IllegalArgumentException: TwitterSession must not be null
at com.twitter.sdk.android.tweetcomposer.ComposerActivity$Builder.session(ComposerActivity.java:95)
at com.example.inextrix.twittereazydemo.MainActivity.onCreate(MainActivity.java:51)
at android.app.Activity.performCreate(Activity.java:6251)
Dr. Droid
Monday, February 11th, 2019Hi Dharmesh,
I think you have not authenticated user before composing. Check this shareUsingTwitterNativeComposer(View view) method where i am checking whether user session is null or not and if it is null then i am authenticating user.
Thanks
Dharmesh Patel`
Tuesday, March 12th, 2019Hello,
Thanks for help me. My above issue is solved app twitter working fine but now after some days I got error that com.twitter.sdk.android.core.TwitterAuthException: Failed to get request token.
Please help to resolved this error.
Dr. Droid
Tuesday, March 12th, 2019Hi Dharmesh,
Can you tell me that it always gives the same error while doing fresh install of the application or on specific scenario this issue is coming?
Thanks
Dharmesh Patel
Tuesday, March 12th, 2019When twitter app is not installed in device then this issue comes(If twitter app is installed then it open app). Moreover, this error I found in log.
Callback URL not approved for this client application. Approved callback URLs can be adjusted in your application settings
Can you please tell me where I need to set callback url in code?
Dr. Droid
Tuesday, March 12th, 2019Hi Dharmesh,
In my previous article about Twitter Auth i mentioned how to set Callback URL.
Actually Callback URL should be set in Apps under Twitter site.
Thanks
Dharmesh Patel
Tuesday, March 12th, 2019Ok. Thanks.
Rishabh Raj
Sunday, July 12th, 2020How can i increase the tweet limit from 140 to 280 as per latest update?
Dharmesh Patel
Friday, August 7th, 2020I faced this error in Android 10.
/sdcard/hive.jpg: open failed: EACCES (Permission denied)
This is provider in Manifest.
provider_paths.xml
Uri in Java class.
Uri data = FileProvider.getUriForFile(this, this.getApplicationContext().getPackageName() + “.provider”, new File(“/sdcard/hive.jpg”));
Dr. Droid
Wednesday, August 12th, 2020Hi Dharmesh,
Have u added the run-time permissions?
Thanks
Fernando
Tuesday, October 13th, 2020Hello Doctor. Thanks for this important update. Please, when I download the source code and upload it to the Studio, I can use the native compose tweet sending images but when I create a new project and do everything in the tutorial I can only use the native compose tweet to send text, when I send an image this does not work and no error appears. I already verify everything is correct according to the tutorial, maybe you know what the problem is? I can send you my code, the donation is guaranteed.
Dr. Droid
Friday, October 16th, 2020Hi Fernando,
Sure please share your code. Let me check it.
Thanks