Skip to content

Commit e99ae93

Browse files
committed
Initial release - Ti.Android.Share module
0 parents  commit e99ae93

23 files changed

Lines changed: 1157 additions & 0 deletions

.gitignore

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Titanium Module Build
2+
build/
3+
build.log
4+
*.pyc
5+
6+
# Gradle
7+
.gradle/
8+
local.properties
9+
10+
# Android Studio / IntelliJ IDEA
11+
.idea/
12+
*.iml
13+
*.ipr
14+
*.iws
15+
.DS_Store
16+
.classpath
17+
.project
18+
.settings/
19+
bin/
20+
21+
# Eclipse
22+
*.launch
23+
.metadata
24+
.recommenders/
25+
26+
# Java
27+
*.class
28+
29+
# Logs
30+
*.log
31+
32+
# OS Files
33+
.DS_Store
34+
._*
35+
Thumbs.db
36+
*~
37+
38+
# Node
39+
node_modules/
40+
41+
# Titanium
42+
tmp/
43+
.fastdev.lock
44+
45+
# Keep distribution ZIPs for releases
46+
!android/dist/*.zip

LICENSE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
TODO: place your license here and we'll include it in the module distribution

README.md

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
# Ti.Android.Share
2+
3+
A native Android module for Titanium SDK that provides seamless image and text sharing using Android's FileProvider API.
4+
5+
![Titanium](https://img.shields.io/badge/Titanium-13.0+-red.svg) ![Platform](https://img.shields.io/badge/platform-Android-lightgrey.svg) ![License](https://img.shields.io/badge/license-MIT-blue.svg) ![Maintained](https://img.shields.io/badge/Maintained-Yes-green.svg)
6+
7+
## Overview
8+
9+
This module solves a critical limitation in Titanium SDK when sharing images on Android 7.0+ (API 24+). Modern Android versions require the use of `content://` URIs through FileProvider instead of `file://` URIs for security reasons. While Titanium SDK provides intent-based sharing capabilities, it lacks native FileProvider support, making image sharing problematic with apps like WhatsApp, Telegram, and email clients.
10+
11+
`ti.android.share` bridges this gap by providing a simple JavaScript API that handles all the complexity of FileProvider configuration, URI generation, and secure file sharing.
12+
13+
## Features
14+
15+
- Share images from Resources, Blobs, or file paths
16+
- Automatic FileProvider URI generation
17+
- Support for text and image combination sharing
18+
- Optional callback to handle share results (success, cancelled, or error)
19+
- Works with WhatsApp, Telegram, Email, and all Android share targets
20+
- Compatible with Titanium SDK 13.x and modern Android versions
21+
22+
## Requirements
23+
24+
- Titanium SDK 13.0.0 or higher
25+
- Android target SDK 24 or higher
26+
27+
## Installation
28+
29+
### Download the module
30+
31+
Download the latest release from the [releases page](https://github.com/deckameron/Ti.Android.Share/releases) or build from source.
32+
33+
### Add to your project
34+
35+
1. Copy the module ZIP file to your project root directory
36+
2. Add the module to your `tiapp.xml`:
37+
38+
```xml
39+
<modules>
40+
<module platform="android">ti.android.share</module>
41+
</modules>
42+
```
43+
44+
### FileProvider configuration
45+
46+
The module automatically configures the FileProvider, but ensure your `tiapp.xml` has a valid application ID:
47+
48+
```xml
49+
<id>com.yourcompany.yourapp</id>
50+
```
51+
52+
The module will use `com.yourcompany.yourapp.fileprovider` as the FileProvider authority.
53+
54+
## Usage
55+
56+
### Basic example
57+
58+
```javascript
59+
const ShareModule = require('ti.android.share');
60+
61+
ShareModule.share({
62+
message: "Check out this amazing content!",
63+
subject: "Sharing from my app",
64+
image: "/images/photo.jpg"
65+
});
66+
```
67+
68+
### Share image from Resources
69+
70+
```javascript
71+
ShareModule.share({
72+
message: "Look at this picture!",
73+
image: "/images/my-image.png"
74+
});
75+
```
76+
77+
### Share image from Blob
78+
79+
```javascript
80+
const imageView = Ti.UI.createImageView({
81+
image: 'https://example.com/image.jpg'
82+
});
83+
84+
// After image loads
85+
imageView.addEventListener('load', function() {
86+
ShareModule.share({
87+
message: "Sharing dynamically loaded image",
88+
image: imageView.toBlob()
89+
});
90+
});
91+
```
92+
93+
### Share text only
94+
95+
```javascript
96+
ShareModule.share({
97+
message: "Just sharing some text",
98+
subject: "Important message"
99+
});
100+
```
101+
102+
### Share with callback
103+
104+
```javascript
105+
ShareModule.share({
106+
message: "Check this out!",
107+
image: "/images/photo.jpg",
108+
subject: "Amazing content",
109+
callback: function(e) {
110+
if (e.success) {
111+
Ti.API.info("Share completed: " + e.message);
112+
// Handle success - maybe track analytics
113+
} else {
114+
Ti.API.error("Share failed: " + e.message);
115+
// Handle cancellation or error
116+
}
117+
}
118+
});
119+
```
120+
121+
## API Reference
122+
123+
### Methods
124+
125+
#### `share(options)`
126+
127+
Opens the Android share dialog with the specified content.
128+
129+
**Parameters:**
130+
131+
- `options` (Object): Configuration object with the following properties:
132+
- `message` (String, optional): Text content to share
133+
- `subject` (String, optional): Subject line for sharing (used by email apps)
134+
- `image` (String|TiBlob, optional): Image to share. Can be:
135+
- Path to resource file (e.g., `/images/photo.jpg`)
136+
- Path from applicationDataDirectory
137+
- TiBlob object (from `imageView.toBlob()`, camera, etc.)
138+
- `callback` (Function, optional): Callback function to receive share result
139+
140+
**Callback object properties:**
141+
142+
- `success` (Boolean): `true` if share completed successfully, `false` if cancelled or failed
143+
- `message` (String): Descriptive message about the share result:
144+
- `"Share completed successfully"` - User completed the share action
145+
- `"Share cancelled by user"` - User cancelled the share dialog
146+
- `"Error: [description]"` - An error occurred during sharing
147+
148+
**Returns:** void
149+
150+
**Example:**
151+
152+
```javascript
153+
ShareModule.share({
154+
message: "Hello World",
155+
subject: "Greeting",
156+
image: myImageBlob,
157+
callback: function(e) {
158+
console.log("Success: " + e.success);
159+
console.log("Message: " + e.message);
160+
}
161+
});
162+
```
163+
164+
## How it works
165+
166+
The module handles the following automatically:
167+
168+
1. **Resource location**: Attempts to find images in multiple locations:
169+
- Application resources (`/app/_app_/Resources/`)
170+
- Android assets
171+
- Application data directory
172+
- Cache directory
173+
174+
2. **File preparation**: For resource files and Blobs:
175+
- Copies the file to the cache directory
176+
- Ensures the file is accessible by the FileProvider
177+
178+
3. **URI generation**: Creates a secure `content://` URI using Android's FileProvider
179+
180+
4. **Permission granting**: Adds `FLAG_GRANT_READ_URI_PERMISSION` so receiving apps can access the file
181+
182+
5. **Intent creation**: Builds a proper `ACTION_SEND` intent with all necessary flags
183+
184+
6. **Result handling**: When a callback is provided, uses `startActivityForResult` to capture the share outcome and notify your application
185+
186+
## Troubleshooting
187+
188+
### Callback not firing
189+
190+
The callback relies on Android's activity result system. Note that:
191+
192+
- `success: true` means the user selected an app and the share intent was delivered
193+
- `success: false` means the user cancelled the share dialog or an error occurred
194+
- Some apps may not properly report back to the activity result system
195+
- The callback indicates the share was initiated, not necessarily that the recipient app completed the action
196+
197+
### Image not found error
198+
199+
If you see logs indicating the image wasn't found, verify:
200+
201+
- The image path is correct (use `/images/photo.jpg`, not `images/photo.jpg`)
202+
- The image exists in your `Resources` folder or specified directory
203+
- Check the `adb logcat` output for detailed path information
204+
205+
### WhatsApp/Telegram not showing image
206+
207+
Ensure:
208+
209+
- The image file is a valid JPEG or PNG
210+
- The file size is reasonable (< 5MB recommended)
211+
- Your app has necessary permissions in `tiapp.xml`:
212+
213+
```xml
214+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
215+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
216+
```
217+
218+
## Contributing
219+
220+
Contributions are welcome! Please feel free to submit a Pull Request.
221+
222+
1. Fork the repository
223+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
224+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
225+
4. Push to the branch (`git push origin feature/amazing-feature`)
226+
5. Open a Pull Request

android/.clang-format

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
Language: Java
3+
AccessModifierOffset: -4
4+
AllowShortBlocksOnASingleLine: false
5+
AllowShortFunctionsOnASingleLine: None
6+
AllowShortIfStatementsOnASingleLine: false
7+
AllowShortLoopsOnASingleLine: false
8+
# class, constructor, method should be next line
9+
BreakBeforeBraces: Linux
10+
# Keep '=' at end of line when wrapping, but move things like '&&', '||' to beginning of newline
11+
BreakBeforeBinaryOperators: NonAssignment
12+
# FIXME: break for brace after synchronized block, anonymous class declarations
13+
BreakAfterJavaFieldAnnotations: true
14+
ColumnLimit: 120
15+
IndentCaseLabels: true
16+
IndentWidth: 4
17+
MaxEmptyLinesToKeep: 1
18+
SpaceBeforeAssignmentOperators: true
19+
SpaceBeforeParens: ControlStatements
20+
SpacesInParentheses: false
21+
TabWidth: 4
22+
UseTab: ForContinuationAndIndentation
23+
SpaceAfterCStyleCast: true
24+
# Spaces inside {} for array literals, i.e. "new Object[] { args }"
25+
Cpp11BracedListStyle: false
26+
ReflowComments: false

android/Resources/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
Files in this directory are copied to the APK's `assets/Resources` when doing an app build.
3+
This is the same directory a Titanium app's `Resources` files are copied to. The app's
4+
`Resources` files take priority and will be copied over module `Resources` files having
5+
the same name.

android/build.gradle

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
repositories {
2+
google()
3+
}
4+
5+
dependencies {
6+
implementation 'androidx.core:core:1.12.0'
7+
}
103 KB
Binary file not shown.
42.6 KB
Binary file not shown.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
distributionBase=GRADLE_USER_HOME
2+
distributionPath=wrapper/dists
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
4+
networkTimeout=10000
5+
validateDistributionUrl=true
6+
zipStoreBase=GRADLE_USER_HOME
7+
zipStorePath=wrapper/dists

0 commit comments

Comments
 (0)