Java Development
The project uses Java as the primary language with Kotlin support. You should be familiar with Android development and Java programming.
Thank you for your interest in contributing to Shipedge Warehouse Pro! This guide helps you understand the project structure, coding standards, and development process. Following these guidelines ensures consistency and quality across the codebase.
This guide covers:
The project uses Java as the primary language with Kotlin support. You should be familiar with Android development and Java programming.
Use Android Studio for development. The project requires Gradle 4.0.0 and Android SDK API 21-29.
Understand both legacy patterns (Activities + AsyncTasks) and modern MVP architecture. New features should use MVP when possible.
This is a production application used in real warehouses. All changes must be tested thoroughly before deployment.
Required Software:
Recommended:
Clone the repository
Clone the repository to your local machine:
git clone [repository-url]cd Android-Documentation/proOpen in Android Studio
Open the pro directory in Android Studio. Android Studio will automatically:
Configure SDK
Ensure you have the required SDK versions installed:
Sync Gradle
Wait for Gradle sync to complete. This downloads all dependencies including:
Build the project
Build the project to verify setup:
./gradlew buildOr use Android Studio: Build → Make Project
Run on device/emulator
Install and run the app on an emulator or physical device to verify everything works.
Key Build Settings:
android {
useLibrary 'org.apache.http.legacy'
compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 }
sourceSets { main { manifest.srcFile 'AndroidManifest.xml' java.srcDirs = ['src'] resources.srcDirs = ['src'] aidl.srcDirs = ['src'] renderscript.srcDirs = ['src'] res.srcDirs = ['res'] assets.srcDirs = ['assets'] }
// Move the tests to tests/java, tests/res, etc... androidTest.setRoot('tests')
// Move the build types to build-types/<type> // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ... // This moves them out of them default location under src/<type>/... which would // conflict with src/ being used by the main source set. // Adding new build types or product flavors should be accompanied // by a similar customization. debug.setRoot('build-types/debug') release.setRoot('build-types/release') } compileSdkVersion 33 defaultConfig{ minSdkVersion 21 multiDexEnabled true targetSdkVersion 34 }
}Classes:
*Activity.java (e.g., PickActivity.java, MyRep.java)*Fragment.java (e.g., CycleCountDetailFragment.java)MyAdapter*.java (e.g., MyAdapterListOrder.java)ConnectToDataBaseServer*.java (e.g., ConnectToDataBaseServerReplenishment.java)Order.java, Product.java, Bin.java)*Presenter.java (interface), *PresenterClass.java (implementation)*Interactor.java (interface), *InteractorClass.java (implementation)Variables:
camelCase for local variablesshippingNumber, productId, binLocationPackages:
shipedge.repshipedge.rep.{module}.{layer} (e.g., shipedge.rep.cycleCountActivity.mainModule.view)shipedge.rep.utilsIndentation:
Line Length:
Comments:
Example:
// Good: Explains business logic// Filter out bins with zero quantity to avoid unnecessary API callsif (bin.getQuantity() > 0) { processBin(bin);}
// Bad: States the obvious// Check if quantity is greater than zeroif (bin.getQuantity() > 0) { processBin(bin);}Legacy Pattern (Existing Features):
MVP Pattern (New Features):
When to Use Each:
See Code Structure for detailed architecture information.
Plan the feature
Understand requirements and design the solution:
Create feature branch
Create a branch from main:
git checkout -b feature/your-feature-nameSet up module structure
For MVP features:
yourFeatureActivity/├── mainModule/│ ├── view/│ ├── presenter/│ ├── model/│ └── events/├── common/│ ├── dataAccess/│ └── params/└── dto/For Legacy features:
res/layout/Implement the feature
Follow the architecture pattern chosen:
Add string resources
CRITICAL: Never hardcode strings. Always use strings.xml:
<string name="your_feature_title">Your Feature Title</string>Reference in Java:
setTitle(getString(R.string.your_feature_title));Test thoroughly
Test on multiple devices/emulators:
Update documentation
Update relevant documentation:
When modifying legacy code:
Understand the current implementation
Make minimal changes
Test regression
Update documentation
Creating API Connection Class:
public class ConnectToDataBaseServerYourFeature { String warehouse;
public ConnectToDataBaseServerYourFeature(String param) { this.warehouse = "https://" + Users.warehouse + ".shipedge.com/android/your-endpoint.php"; }
public YourModel[] getData() { // HTTP POST request // Parse JSON response // Return model array }}Using in Activity:
// In AsyncTask or background threadConnectToDataBaseServerYourFeature api = new ConnectToDataBaseServerYourFeature(param);YourModel[] data = api.getData();// Update UI on main threadCreate Retrofit Service:
public interface YourFeatureService { @POST("your-endpoint.php") Call<YourResponse> getData( @Body YourParams params, @Header("token") String token );}Create API Client:
public class YourFeatureAPI { private YourFeatureService service;
public YourFeatureAPI(String token, String warehouse) { service = HttpClient.createService( YourFeatureService.class, UtilService.getEndpoint(warehouse) ); }
public void getData(YourParams params, EventCallback callback) { Call<YourResponse> call = service.getData(params, token); call.enqueue(new Callback<YourResponse>() { // Handle response }); }}See API Reference for detailed API documentation.
Always use string resources:
<string name="button_save">Save</string><string name="error_network">Network error. Please try again.</string>Reference in Java:
button.setText(getString(R.string.button_save));Toast.makeText(context, getString(R.string.error_network), Toast.LENGTH_SHORT).show();Reference in XML:
<Button android:text="@string/button_save" />Naming Convention:
activity_*.xmlfragment_*.xmlitem_*.xmldialog_*.xmlExample:
activity_pick.xml - PickActivity layoutfragment_cycle_count_detail.xml - CycleCountDetailFragment layoutitem_order_list.xml - Order list item layoutdrawable-mdpi/ - Medium densitydrawable-hdpi/ - High densitydrawable-xhdpi/ - Extra high densitydrawable-xxhdpi/ - Extra extra high densityTest Checklist:
Network Testing:
Data Testing:
Hardware Testing:
Checklist:
Include:
Example:
## WhatAdded search functionality to Returns screen
## WhyUsers need to search returns by tracking number, shipping number, or RMA
## How- Added search fields to ReturnActivity- Created API call to search endpoint- Updated UI to display search results
## Testing- Tested search by tracking number ✓- Tested search by shipping number ✓- Tested search by RMA ✓- Tested error handling ✓- Tested on API 21 and API 29 ✓What reviewers check:
Responding to feedback:
When creating new features, use MVP architecture pattern like Cycle Count and Load & Move modules. It provides better separation of concerns and testability.
Never hardcode strings in Java code or XML layouts. Always use strings.xml for all user-facing text. This enables internationalization and consistency.
Always handle network errors, invalid data, and edge cases. Show user-friendly error messages from string resources.
Test on physical devices when possible, especially for Bluetooth scanner features. Emulators may not accurately represent real-world behavior.
Each Activity should have a single responsibility. If an Activity becomes too large (1000+ lines), consider splitting into multiple Activities or using MVP pattern.
Add comments for complex business logic, especially in legacy code. MVP pattern makes code self-documenting through clear separation.
When modifying existing features, follow the existing patterns and conventions. Don’t introduce new patterns unless refactoring the entire feature.
Be mindful of performance, especially for operations that run on the main thread. Use AsyncTask or background threads for network calls and heavy processing.
Hardcoding Strings:
// BADbutton.setText("Save");Toast.makeText(context, "Error occurred", Toast.LENGTH_SHORT).show();Blocking Main Thread:
// BADString result = apiCall.getData(); // Blocks UI threadupdateUI(result);Ignoring Errors:
// BADtry { processData();} catch (Exception e) { // Silent failure}Large Activities:
// BAD - Activity with 2000+ lines mixing UI, business logic, and data accesspublic class MyRep extends Activity { // 2000 lines of mixed concerns}Use String Resources:
// GOODbutton.setText(getString(R.string.button_save));Toast.makeText(context, getString(R.string.error_occurred), Toast.LENGTH_SHORT).show();Use Background Threads:
// GOODnew AsyncTask<Void, Void, String>() { @Override protected String doInBackground(Void... params) { return apiCall.getData(); // Background thread }
@Override protected void onPostExecute(String result) { updateUI(result); // Main thread }}.execute();Handle Errors Properly:
// GOODtry { processData();} catch (NetworkException e) { showError(getString(R.string.error_network));} catch (Exception e) { Log.e(TAG, "Unexpected error", e); showError(getString(R.string.error_unexpected));}Use MVP Pattern:
// GOOD - Separated concernspublic class CycleCountActivity extends AppCompatActivity implements CycleCountView { private CycleCountPresenter presenter; // View only handles UI}
public class CycleCountPresenter { private CycleCountInteractor interactor; // Presenter handles business logic}If you have questions: