Port FeatureOverview to Kotlin and remove empty activity tests (#686)
This commit is contained in:
@@ -259,11 +259,6 @@ run-android-publish: gradle/configuration.gradle
|
||||
android-gfxinfo:
|
||||
adb shell dumpsys gfxinfo com.mapbox.mapboxsdk.testapp reset
|
||||
|
||||
# Generates Activity sanity tests
|
||||
.PHONY: test-code-android
|
||||
test-code-android:
|
||||
node scripts/generate-test-code.js
|
||||
|
||||
# Runs checkstyle and lint on the java code
|
||||
.PHONY: android-check
|
||||
android-check : android-ktlint android-checkstyle android-lint-sdk android-lint-test-app run-android-nitpick
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
<%
|
||||
const activity = locals[0];
|
||||
const subPackage = locals[1];
|
||||
-%>
|
||||
// This file is generated. Edit android/platform/scripts/generate-test-code.js, then run `make generate-test-android`.
|
||||
package com.mapbox.mapboxsdk.testapp.activity.gen.<%- subPackage %>;
|
||||
|
||||
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
|
||||
import com.mapbox.mapboxsdk.testapp.activity.BaseTest
|
||||
import com.mapbox.mapboxsdk.testapp.activity.<%- subPackage %>.<%- activity %>
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
/**
|
||||
* Sanity test for <%- activity %>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4ClassRunner::class)
|
||||
class <%- activity %>Test : BaseTest() {
|
||||
@Test
|
||||
fun testSanity() {
|
||||
}
|
||||
|
||||
override fun getActivityClass(): Class<*> {
|
||||
return <%- activity %>::class.java
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
package com.mapbox.mapboxsdk.testapp.activity;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.mapbox.mapboxsdk.testapp.R;
|
||||
import com.mapbox.mapboxsdk.testapp.adapter.FeatureAdapter;
|
||||
import com.mapbox.mapboxsdk.testapp.adapter.FeatureSectionAdapter;
|
||||
import com.mapbox.mapboxsdk.testapp.model.activity.Feature;
|
||||
import com.mapbox.mapboxsdk.testapp.utils.ItemClickSupport;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Activity shown when application is started
|
||||
* <p>
|
||||
* This activity will generate data for RecyclerView based on the AndroidManifest entries.
|
||||
* It uses tags as category and description to order the different entries.
|
||||
* </p>
|
||||
*/
|
||||
public class FeatureOverviewActivity extends AppCompatActivity {
|
||||
|
||||
private static final String KEY_STATE_FEATURES = "featureList";
|
||||
|
||||
private RecyclerView recyclerView;
|
||||
private FeatureSectionAdapter sectionAdapter;
|
||||
private List<Feature> features;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_feature_overview);
|
||||
|
||||
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
recyclerView.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener());
|
||||
recyclerView.setHasFixedSize(true);
|
||||
|
||||
ItemClickSupport.addTo(recyclerView).setOnItemClickListener((recyclerView, position, view) -> {
|
||||
if (!sectionAdapter.isSectionHeaderPosition(position)) {
|
||||
int itemPosition = sectionAdapter.getConvertedPosition(position);
|
||||
Feature feature = features.get(itemPosition);
|
||||
startFeature(feature);
|
||||
}
|
||||
});
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
loadFeatures();
|
||||
} else {
|
||||
features = savedInstanceState.getParcelableArrayList(KEY_STATE_FEATURES);
|
||||
onFeaturesLoaded(features);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFeatures() {
|
||||
try {
|
||||
new LoadFeatureTask().execute(
|
||||
getPackageManager().getPackageInfo(getPackageName(),
|
||||
PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA));
|
||||
} catch (PackageManager.NameNotFoundException exception) {
|
||||
Timber.e(exception, "Could not resolve package info");
|
||||
}
|
||||
}
|
||||
|
||||
private void onFeaturesLoaded(List<Feature> featuresList) {
|
||||
features = featuresList;
|
||||
if (featuresList == null || featuresList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<FeatureSectionAdapter.Section> sections = new ArrayList<>();
|
||||
String currentCat = "";
|
||||
for (int i = 0; i < features.size(); i++) {
|
||||
String category = features.get(i).getCategory();
|
||||
if (!currentCat.equals(category)) {
|
||||
sections.add(new FeatureSectionAdapter.Section(i, category));
|
||||
currentCat = category;
|
||||
}
|
||||
}
|
||||
|
||||
FeatureSectionAdapter.Section[] dummy = new FeatureSectionAdapter.Section[sections.size()];
|
||||
sectionAdapter = new FeatureSectionAdapter(
|
||||
this, R.layout.section_main_layout, R.id.section_text, new FeatureAdapter(features));
|
||||
sectionAdapter.setSections(sections.toArray(dummy));
|
||||
recyclerView.setAdapter(sectionAdapter);
|
||||
}
|
||||
|
||||
private void startFeature(Feature feature) {
|
||||
Intent intent = new Intent();
|
||||
intent.setComponent(new ComponentName(getPackageName(), feature.getName()));
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putParcelableArrayList(KEY_STATE_FEATURES, (ArrayList<Feature>) features);
|
||||
}
|
||||
|
||||
private class LoadFeatureTask extends AsyncTask<PackageInfo, Void, List<Feature>> {
|
||||
|
||||
@Override
|
||||
protected List<Feature> doInBackground(PackageInfo... params) {
|
||||
List<Feature> features = new ArrayList<>();
|
||||
PackageInfo app = params[0];
|
||||
|
||||
String packageName = getApplicationContext().getPackageName();
|
||||
String metaDataKey = getString(R.string.category);
|
||||
for (ActivityInfo info : app.activities) {
|
||||
if (info.labelRes != 0 && info.name.startsWith(packageName)
|
||||
&& !info.name.equals(FeatureOverviewActivity.class.getName())) {
|
||||
String label = getString(info.labelRes);
|
||||
String description = resolveString(info.descriptionRes);
|
||||
String category = resolveMetaData(info.metaData, metaDataKey);
|
||||
features.add(new Feature(info.name, label, description, category));
|
||||
}
|
||||
}
|
||||
|
||||
if (!features.isEmpty()) {
|
||||
Comparator<Feature> comparator = (lhs, rhs) -> {
|
||||
int result = lhs.getCategory().compareToIgnoreCase(rhs.getCategory());
|
||||
if (result == 0) {
|
||||
result = lhs.getLabel().compareToIgnoreCase(rhs.getLabel());
|
||||
}
|
||||
return result;
|
||||
};
|
||||
Collections.sort(features, comparator);
|
||||
}
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
private String resolveMetaData(Bundle bundle, String key) {
|
||||
String category = null;
|
||||
if (bundle != null) {
|
||||
category = bundle.getString(key);
|
||||
}
|
||||
return category;
|
||||
}
|
||||
|
||||
private String resolveString(@StringRes int stringRes) {
|
||||
try {
|
||||
return getString(stringRes);
|
||||
} catch (Resources.NotFoundException exception) {
|
||||
return "-";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(List<Feature> features) {
|
||||
super.onPostExecute(features);
|
||||
onFeaturesLoaded(features);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
package com.mapbox.mapboxsdk.testapp.activity
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Resources.NotFoundException
|
||||
import android.os.AsyncTask
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.SimpleOnItemTouchListener
|
||||
import com.mapbox.mapboxsdk.testapp.R
|
||||
import com.mapbox.mapboxsdk.testapp.adapter.FeatureAdapter
|
||||
import com.mapbox.mapboxsdk.testapp.adapter.FeatureSectionAdapter
|
||||
import com.mapbox.mapboxsdk.testapp.model.activity.Feature
|
||||
import com.mapbox.mapboxsdk.testapp.utils.ItemClickSupport
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Activity shown when application is started
|
||||
*
|
||||
*
|
||||
* This activity will generate data for RecyclerView based on the AndroidManifest entries.
|
||||
* It uses tags as category and description to order the different entries.
|
||||
*
|
||||
*/
|
||||
class FeatureOverviewActivity : AppCompatActivity() {
|
||||
private var recyclerView: RecyclerView? = null
|
||||
private var sectionAdapter: FeatureSectionAdapter? = null
|
||||
private var features: List<Feature>? = null
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_feature_overview)
|
||||
recyclerView = findViewById<View>(R.id.recyclerView) as RecyclerView
|
||||
recyclerView!!.layoutManager = LinearLayoutManager(this)
|
||||
recyclerView!!.addOnItemTouchListener(SimpleOnItemTouchListener())
|
||||
recyclerView!!.setHasFixedSize(true)
|
||||
ItemClickSupport.addTo(recyclerView)
|
||||
.setOnItemClickListener { recyclerView: RecyclerView?, position: Int, view: View? ->
|
||||
if (!sectionAdapter!!.isSectionHeaderPosition(position)) {
|
||||
val itemPosition = sectionAdapter!!.getConvertedPosition(position)
|
||||
val feature = features!![itemPosition]
|
||||
startFeature(feature)
|
||||
}
|
||||
}
|
||||
if (savedInstanceState == null) {
|
||||
loadFeatures()
|
||||
} else {
|
||||
features = savedInstanceState.getParcelableArrayList(KEY_STATE_FEATURES)
|
||||
onFeaturesLoaded(features)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadFeatures() {
|
||||
try {
|
||||
LoadFeatureTask().execute(
|
||||
packageManager.getPackageInfo(
|
||||
packageName,
|
||||
PackageManager.GET_ACTIVITIES or PackageManager.GET_META_DATA
|
||||
)
|
||||
)
|
||||
} catch (exception: PackageManager.NameNotFoundException) {
|
||||
Timber.e(exception, "Could not resolve package info")
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||
private fun onFeaturesLoaded(featuresList: List<Feature>?) {
|
||||
features = featuresList
|
||||
if (featuresList == null || featuresList.isEmpty()) {
|
||||
return
|
||||
}
|
||||
val sections: MutableList<FeatureSectionAdapter.Section> = ArrayList()
|
||||
var currentCat = ""
|
||||
for (i in features!!.indices) {
|
||||
val category = features!![i].category
|
||||
if (currentCat != category) {
|
||||
sections.add(FeatureSectionAdapter.Section(i, category))
|
||||
currentCat = category
|
||||
}
|
||||
}
|
||||
|
||||
sectionAdapter = FeatureSectionAdapter(
|
||||
this,
|
||||
R.layout.section_main_layout,
|
||||
R.id.section_text,
|
||||
FeatureAdapter(features!!)
|
||||
)
|
||||
sectionAdapter!!.setSections(sections.toTypedArray())
|
||||
recyclerView!!.adapter = sectionAdapter
|
||||
}
|
||||
|
||||
private fun startFeature(feature: Feature) {
|
||||
val intent = Intent()
|
||||
intent.component = ComponentName(packageName, feature.name)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putParcelableArrayList(KEY_STATE_FEATURES, features as ArrayList<Feature>?)
|
||||
}
|
||||
|
||||
private inner class LoadFeatureTask : AsyncTask<PackageInfo?, Void?, List<Feature>>() {
|
||||
protected override fun doInBackground(vararg p0: PackageInfo?): List<Feature>? {
|
||||
val features: MutableList<Feature> = ArrayList()
|
||||
val app = p0[0]
|
||||
val packageName = applicationContext.packageName
|
||||
val metaDataKey = getString(R.string.category)
|
||||
if (app != null) {
|
||||
for (info in app.activities) {
|
||||
if (info.labelRes != 0 && info.name.startsWith(packageName) &&
|
||||
info.name != FeatureOverviewActivity::class.java.name
|
||||
) {
|
||||
val label = getString(info.labelRes)
|
||||
val description = resolveString(info.descriptionRes)
|
||||
val category = resolveMetaData(info.metaData, metaDataKey)
|
||||
features.add(Feature(info.name, label, description, category!!))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!features.isEmpty()) {
|
||||
val comparator = Comparator { lhs: Feature, rhs: Feature ->
|
||||
var result = lhs.category.compareTo(rhs.category, ignoreCase = true)
|
||||
if (result == 0) {
|
||||
result = lhs.getLabel().compareTo(rhs.getLabel(), ignoreCase = true)
|
||||
}
|
||||
result
|
||||
}
|
||||
Collections.sort(features, comparator)
|
||||
}
|
||||
return features
|
||||
}
|
||||
|
||||
private fun resolveMetaData(bundle: Bundle?, key: String): String? {
|
||||
var category: String? = null
|
||||
if (bundle != null) {
|
||||
category = bundle.getString(key)
|
||||
}
|
||||
return category
|
||||
}
|
||||
|
||||
private fun resolveString(@StringRes stringRes: Int): String {
|
||||
return try {
|
||||
getString(stringRes)
|
||||
} catch (exception: NotFoundException) {
|
||||
"-"
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||
override fun onPostExecute(features: List<Feature>) {
|
||||
super.onPostExecute(features)
|
||||
onFeaturesLoaded(features)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_STATE_FEATURES = "featureList"
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.mapbox.mapboxsdk.testapp.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.mapbox.mapboxsdk.testapp.activity.FeatureOverviewActivity;
|
||||
|
||||
public class NavUtils {
|
||||
|
||||
public static void navigateHome(@NonNull Activity context) {
|
||||
context.startActivity(new Intent(context, FeatureOverviewActivity.class));
|
||||
context.finish();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.mapbox.mapboxsdk.testapp.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import com.mapbox.mapboxsdk.testapp.activity.FeatureOverviewActivity
|
||||
|
||||
object NavUtils {
|
||||
fun navigateHome(context: Activity) {
|
||||
context.startActivity(Intent(context, FeatureOverviewActivity::class.java))
|
||||
context.finish()
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
[
|
||||
"BaseLocationActivity",
|
||||
"MapSnapshotterMarkerActivity",
|
||||
"MapSnapshotterReuseActivity",
|
||||
"MapSnapshotterLocalStyleActivity",
|
||||
"LatLngBoundsActivity",
|
||||
"BottomSheetActivity",
|
||||
"MockLocationEngine",
|
||||
"DeleteRegionActivity",
|
||||
"RealTimeGeoJsonActivity",
|
||||
"UpdateMetadataActivity",
|
||||
"CarDrivingActivity",
|
||||
"MyLocationTrackingModeActivity",
|
||||
"MyLocationToggleActivity",
|
||||
"MyLocationTintActivity",
|
||||
"MyLocationDrawableActivity",
|
||||
"DoubleMapActivity",
|
||||
"LocationPickerActivity",
|
||||
"GeoJsonClusteringActivity",
|
||||
"RuntimeStyleTestActivity",
|
||||
"AnimatedSymbolLayerActivity",
|
||||
"ViewPagerActivity",
|
||||
"MapFragmentActivity",
|
||||
"SupportMapFragmentActivity",
|
||||
"SnapshotActivity",
|
||||
"NavigationDrawerActivity",
|
||||
"QueryRenderedFeaturesBoxHighlightActivity",
|
||||
"MultiMapActivity",
|
||||
"MapInDialogActivity",
|
||||
"ManualZoomActivity",
|
||||
"MaxMinZoomActivity",
|
||||
"ScrollByActivity",
|
||||
"ZoomFunctionSymbolLayerActivity",
|
||||
"SymbolGeneratorActivity",
|
||||
"TextureViewTransparentBackgroundActivity",
|
||||
"SimpleMapActivity",
|
||||
"RenderTestActivity",
|
||||
"SymbolLayerActivity",
|
||||
"LocationFragmentActivity",
|
||||
"MergeOfflineRegionsActivity",
|
||||
"NestedViewPagerActivity",
|
||||
"DebugModeActivity",
|
||||
"TextureViewDebugModeActivity",
|
||||
"RuntimeStyleActivity",
|
||||
"GridSourceActivity",
|
||||
"AnimatedImageSourceActivity",
|
||||
"EspressoTestActivity",
|
||||
"ChangeResourcesCachePathActivity",
|
||||
"EspressoTestActivity",
|
||||
"FragmentBackStackActivity",
|
||||
"ChildFragmentMapInDialogActivity",
|
||||
"PerformanceMeasurementActivity"
|
||||
]
|
||||
@@ -1,73 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const ejs = require('ejs');
|
||||
|
||||
global.iff = function (condition, val) {
|
||||
return condition() ? val : "";
|
||||
}
|
||||
|
||||
global.camelize = function (str) {
|
||||
return str.replace(/(?:^|-)(.)/g, function (_, x) {
|
||||
return x.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
const excludeClasses = JSON.parse(fs.readFileSync('scripts/exclude-activity-gen.json', 'utf8'));
|
||||
const appBasePath = 'MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity';
|
||||
const testBasePath = 'MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/gen';
|
||||
const subPackages = fs.readdirSync(appBasePath);
|
||||
const ejsConversionTask = ejs.compile(fs.readFileSync('MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/activity.junit.ejs', 'utf8'), {strict: true});
|
||||
|
||||
if (!fs.existsSync(testBasePath)){
|
||||
fs.mkdirSync(testBasePath);
|
||||
}
|
||||
|
||||
console.log("\nGenerating test activities:\n");
|
||||
var generatedClasses = [];
|
||||
var excludedClasses = [];
|
||||
for(const subPackage of subPackages) {
|
||||
if(!(subPackage.slice(-5) === '.java')) {
|
||||
const activities = fs.readdirSync(`${appBasePath}/${subPackage}`);
|
||||
|
||||
// create directories for package
|
||||
if (!fs.existsSync(`${testBasePath}/${subPackage}`)){
|
||||
fs.mkdirSync(`${testBasePath}/${subPackage}`);
|
||||
}
|
||||
|
||||
for (const activity of activities) {
|
||||
var activityName;
|
||||
if (activity.slice(-5) === '.java') {
|
||||
// .java file
|
||||
activityName = activity.slice(0, -5);
|
||||
} else {
|
||||
// .java file
|
||||
activityName = activity.slice(0, -3);
|
||||
}
|
||||
|
||||
// create path for test file
|
||||
const filePath = `${testBasePath}/${subPackage}/${activityName}Test.kt`;
|
||||
|
||||
// try removing previous generated files
|
||||
try {
|
||||
fs.accessSync(filePath, fs.F_OK);
|
||||
fs.unlinkSync(filePath);
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
// only generate test file if not part of exclude list + if contains Activity in name
|
||||
if ((!(excludeClasses.indexOf(activityName) > -1)) && activityName.includes("Activity")) {
|
||||
fs.writeFileSync(filePath, ejsConversionTask([activityName, subPackage]));
|
||||
generatedClasses.push(activityName);
|
||||
}else{
|
||||
excludedClasses.push(activityName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(const generatedClass of generatedClasses){
|
||||
console.log(`${generatedClass}Test`);
|
||||
}
|
||||
|
||||
console.log(`\nFinished generating ${generatedClasses.length} activity sanity tests, excluded ${excludeClasses.length} classes.\n`);
|
||||
Reference in New Issue
Block a user