diff --git a/.idea/modules.xml b/.idea/modules.xml
index deb8081..5fd4831 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -4,6 +4,7 @@
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index b090563..c9f2ddd 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -8,8 +8,9 @@ android {
applicationId "news.androidtv.tvapprepo"
minSdkVersion 21
targetSdkVersion 25
- versionCode 16
- versionName "1.1.3"
+ versionCode 17
+ versionName "1.1.4"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
@@ -73,6 +74,11 @@ dependencies {
compile('com.crashlytics.sdk.android:crashlytics:2.6.5@aar') {
transitive = true;
}
+
+ androidTestCompile 'com.android.support:support-annotations:25.3.1'
+ androidTestCompile 'com.android.support.test:runner:0.5'
+ androidTestCompile 'com.android.support.test:rules:0.5'
+ androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
}
apply plugin: 'com.google.gms.google-services'
diff --git a/app/src/androidTest/java/news/androidtv/tvapprepo/ApplicationTest.java b/app/src/androidTest/java/news/androidtv/tvapprepo/IntentsInstrumentationTest.java
similarity index 87%
rename from app/src/androidTest/java/news/androidtv/tvapprepo/ApplicationTest.java
rename to app/src/androidTest/java/news/androidtv/tvapprepo/IntentsInstrumentationTest.java
index 35db6e0..1f086b9 100644
--- a/app/src/androidTest/java/news/androidtv/tvapprepo/ApplicationTest.java
+++ b/app/src/androidTest/java/news/androidtv/tvapprepo/IntentsInstrumentationTest.java
@@ -1,52 +1,51 @@
-package news.androidtv.tvapprepo;
-
-import android.app.Application;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.test.ApplicationTestCase;
-import android.util.Log;
-
-import java.io.File;
-import java.net.URISyntaxException;
-
-import dalvik.annotation.TestTargetClass;
-import news.androidtv.tvapprepo.intents.IntentUriGenerator;
-
-/**
- * Testing Fundamentals
- */
-public class ApplicationTest extends ApplicationTestCase {
- public static final String TAG = ApplicationTest.class.getSimpleName();
-
- public ApplicationTest() {
- super(Application.class);
- }
-
- public void testWebBookmarks() {
- final String expected = "intent://google.com#Intent;scheme=http;end";
- String actual = IntentUriGenerator.generateWebBookmark("http://google.com");
- Log.d(TAG, actual);
- assertEquals(expected, actual);
- }
-
- public void testActivityShortcut() {
- final String expected = "intent:#Intent;component=news.androidtv.tvapprepo/.activities.SettingsActivity;end";
- String actual = IntentUriGenerator.generateActivityShortcut(new ComponentName("news.androidtv.tvapprepo", ".activities.SettingsActivity"));
- Log.d(TAG, actual);
- assertEquals(expected, actual);
- }
-
- public void testFileOpening() {
- // Note: This can be flaky if your device doesn't have this file. Future versions of this
- // test should create and delete a temporary file.
- final String expected = "intent:///storage/emulated/0/Download/com.felkertech.n.cumulustv.test.apk#Intent;scheme=file;launchFlags=0x10000000;end";
- String actual = IntentUriGenerator.generateVideoPlayback(new File("/storage/emulated/0/Download/com.felkertech.n.cumulustv.test.apk"));
- Log.d(TAG, actual);
- assertEquals(expected, actual);
- }
-
- public void testOpenGoogle() throws URISyntaxException {
- String string = "intent:#Intent;component=news.androidtv.tvapprepo/.activities.SettingsActivity;end";
- getContext().startActivity(Intent.parseUri(string, Intent.URI_INTENT_SCHEME));
- }
+package news.androidtv.tvapprepo;
+
+import android.app.Application;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.test.ApplicationTestCase;
+import android.util.Log;
+
+import java.io.File;
+import java.net.URISyntaxException;
+
+import news.androidtv.tvapprepo.intents.IntentUriGenerator;
+
+/**
+ * Testing Fundamentals
+ */
+public class IntentsInstrumentationTest extends ApplicationTestCase {
+ public static final String TAG = IntentsInstrumentationTest.class.getSimpleName();
+
+ public IntentsInstrumentationTest() {
+ super(Application.class);
+ }
+
+ public void testWebBookmarks() {
+ final String expected = "intent://google.com#Intent;scheme=http;end";
+ String actual = IntentUriGenerator.generateWebBookmark("http://google.com");
+ Log.d(TAG, actual);
+ assertEquals(expected, actual);
+ }
+
+ public void testActivityShortcut() {
+ final String expected = "intent:#Intent;component=news.androidtv.tvapprepo/.activities.SettingsActivity;end";
+ String actual = IntentUriGenerator.generateActivityShortcut(new ComponentName("news.androidtv.tvapprepo", ".activities.SettingsActivity"));
+ Log.d(TAG, actual);
+ assertEquals(expected, actual);
+ }
+
+ public void testFileOpening() {
+ // Note: This can be flaky if your device doesn't have this file. Future versions of this
+ // test should create and delete a temporary file.
+ final String expected = "intent:///storage/emulated/0/Download/com.felkertech.n.cumulustv.test.apk#Intent;scheme=file;launchFlags=0x10000000;end";
+ String actual = IntentUriGenerator.generateVideoPlayback(new File("/storage/emulated/0/Download/com.felkertech.n.cumulustv.test.apk"));
+ Log.d(TAG, actual);
+ assertEquals(expected, actual);
+ }
+
+ public void testOpenGoogle() throws URISyntaxException {
+ String string = "intent:#Intent;component=news.androidtv.tvapprepo/.activities.SettingsActivity;end";
+ getContext().startActivity(Intent.parseUri(string, Intent.URI_INTENT_SCHEME));
+ }
}
\ No newline at end of file
diff --git a/app/src/androidTest/java/news/androidtv/tvapprepo/ShortcutGenerationInstrumentationTest.java b/app/src/androidTest/java/news/androidtv/tvapprepo/ShortcutGenerationInstrumentationTest.java
new file mode 100644
index 0000000..71665fc
--- /dev/null
+++ b/app/src/androidTest/java/news/androidtv/tvapprepo/ShortcutGenerationInstrumentationTest.java
@@ -0,0 +1,205 @@
+package news.androidtv.tvapprepo;
+
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.graphics.Bitmap;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import com.android.volley.VolleyError;
+import com.bumptech.glide.Glide;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import news.androidtv.tvapprepo.activities.MainActivity;
+import news.androidtv.tvapprepo.intents.IntentUriGenerator;
+import news.androidtv.tvapprepo.model.AdvancedOptions;
+import news.androidtv.tvapprepo.utils.GenerateShortcutHelper;
+
+/**
+ * A series of tests relating to generating shortcut apks.
+ * Right now all tests must be verified manually.
+ */
+@RunWith(AndroidJUnit4.class)
+public class ShortcutGenerationInstrumentationTest {
+ @Rule
+ public ActivityTestRule mActivityRule = new ActivityTestRule<>(
+ MainActivity.class);
+
+ private static final String TAG = ShortcutGenerationInstrumentationTest.class.getSimpleName();
+ private static final int TIMEOUT_LENGTH = 90;
+ private static final TimeUnit TIMEOUT_UNITS = TimeUnit.SECONDS;
+ private static final ResolveInfo SAMPLE_APK;
+ private static final String SAMPLE_BANNER = "http://via.placeholder.com/320x180";
+
+ static {
+ SAMPLE_APK = new ResolveInfo();
+ SAMPLE_APK.activityInfo = new ActivityInfo();
+ SAMPLE_APK.activityInfo.applicationInfo = new ApplicationInfo();
+ SAMPLE_APK.activityInfo.applicationInfo.packageName = "com.test";
+ SAMPLE_APK.activityInfo.nonLocalizedLabel = "Test Activity Label";
+ SAMPLE_APK.activityInfo.name = "Test Activity Name";
+ SAMPLE_APK.icon = R.drawable.ic_launcher;
+ }
+
+ private void doOnMainThread(Runnable r) {
+ mActivityRule.getActivity().runOnUiThread(r);
+ }
+
+ /**
+ * Generates an APK shortcut from an on-device APK without advanced options.
+ */
+ @Test
+ public void testSimpleApkGeneration() throws InterruptedException {
+ // Set up a CountdownLatch to accommodate for network events
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ doOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ GenerateShortcutHelper.generateShortcut(mActivityRule.getActivity(), SAMPLE_APK,
+ new AdvancedOptions(mActivityRule.getActivity()), new GenerateShortcutHelper.Callback() {
+ @Override
+ public void onResponseComplete(String response) {
+ latch.countDown();
+ }
+
+ @Override
+ public void onResponseFailed(VolleyError error) {
+ throw new RuntimeException(error.getMessage());
+ }
+ });
+ }
+ });
+
+ Assert.assertTrue(latch.await(TIMEOUT_LENGTH, TIMEOUT_UNITS));
+ }
+
+ @Test
+ public void testCustomBannerApkGeneration() throws InterruptedException {
+ // Set up a CountdownLatch to accommodate for network events
+ final CountDownLatch latch = new CountDownLatch(1);
+ final AdvancedOptions advancedOptions = new AdvancedOptions(mActivityRule.getActivity());
+ advancedOptions.setBannerUrl(SAMPLE_BANNER);
+
+ doOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ GenerateShortcutHelper.generateShortcut(mActivityRule.getActivity(), SAMPLE_APK,
+ advancedOptions, new GenerateShortcutHelper.Callback() {
+ @Override
+ public void onResponseComplete(String response) {
+ Log.d(TAG, response);
+ latch.countDown();
+ }
+
+ @Override
+ public void onResponseFailed(VolleyError error) {
+ throw new RuntimeException(error.getMessage());
+ }
+ });
+ }
+ });
+
+ Assert.assertTrue(latch.await(TIMEOUT_LENGTH, TIMEOUT_UNITS));
+ }
+
+ @Test
+ public void testGameApkGeneration() throws InterruptedException {
+ // Set up a CountdownLatch to accommodate for network events
+ final CountDownLatch latch = new CountDownLatch(1);
+ final AdvancedOptions advancedOptions = new AdvancedOptions(mActivityRule.getActivity());
+ advancedOptions.setIsGame(true);
+
+ doOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ GenerateShortcutHelper.generateShortcut(mActivityRule.getActivity(), SAMPLE_APK,
+ advancedOptions, new GenerateShortcutHelper.Callback() {
+ @Override
+ public void onResponseComplete(String response) {
+ latch.countDown();
+ }
+
+ @Override
+ public void onResponseFailed(VolleyError error) {
+ throw new RuntimeException(error.getMessage());
+ }
+ });
+ }
+ });
+
+ Assert.assertTrue(latch.await(TIMEOUT_LENGTH, TIMEOUT_UNITS));
+ }
+
+ @Test
+ public void testBannerBitmapApk() throws InterruptedException, ExecutionException {
+ // Set up a CountdownLatch to accommodate for network events
+ final CountDownLatch latch = new CountDownLatch(1);
+ final AdvancedOptions advancedOptions = new AdvancedOptions(mActivityRule.getActivity());
+ Bitmap bitmap = Glide.with(mActivityRule.getActivity()).load(SAMPLE_BANNER).asBitmap()
+ .into(320, 180).get();
+ advancedOptions.setBannerBitmap(bitmap);
+
+ doOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ GenerateShortcutHelper.generateShortcut(mActivityRule.getActivity(), SAMPLE_APK,
+ advancedOptions, new GenerateShortcutHelper.Callback() {
+ @Override
+ public void onResponseComplete(String response) {
+ latch.countDown();
+ }
+
+ @Override
+ public void onResponseFailed(VolleyError error) {
+ throw new RuntimeException(error.getMessage());
+ }
+ });
+ }
+ });
+
+ Assert.assertTrue(latch.await(TIMEOUT_LENGTH, TIMEOUT_UNITS));
+ }
+
+ @Test
+ public void testWebShortcut() throws InterruptedException, ExecutionException {
+ // Set up a CountdownLatch to accommodate for network events
+ final CountDownLatch latch = new CountDownLatch(1);
+ String url = "http://example.com";
+ String label = "Example.Com";
+ final AdvancedOptions advancedOptions = new AdvancedOptions(mActivityRule.getActivity())
+ .setIntentUri(IntentUriGenerator.generateWebBookmark(url))
+ .setIconUrl("https://raw.githubusercontent.com/ITVlab/TvAppRepo/master/promo/graphics/icon.png")
+ .setCustomLabel(label);
+
+ doOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ GenerateShortcutHelper.generateShortcut(mActivityRule.getActivity(), SAMPLE_APK,
+ advancedOptions, new GenerateShortcutHelper.Callback() {
+ @Override
+ public void onResponseComplete(String response) {
+ latch.countDown();
+ }
+
+ @Override
+ public void onResponseFailed(VolleyError error) {
+ throw new RuntimeException(error.getMessage());
+ }
+ });
+ }
+ });
+
+ Assert.assertTrue(latch.await(TIMEOUT_LENGTH, TIMEOUT_UNITS));
+ }
+}
diff --git a/app/src/main/java/news/androidtv/tvapprepo/fragments/MainFragment.java b/app/src/main/java/news/androidtv/tvapprepo/fragments/MainFragment.java
index 1f12d8f..60fad95 100644
--- a/app/src/main/java/news/androidtv/tvapprepo/fragments/MainFragment.java
+++ b/app/src/main/java/news/androidtv/tvapprepo/fragments/MainFragment.java
@@ -145,6 +145,7 @@ public void onDestroy() {
mBackgroundTimer.cancel();
}
mApkDownloadHelper.removeListener(mDownloadListener);
+ mApkDownloadHelper.destroy();
}
@Override
diff --git a/app/src/main/java/news/androidtv/tvapprepo/utils/GenerateShortcutHelper.java b/app/src/main/java/news/androidtv/tvapprepo/utils/GenerateShortcutHelper.java
index 683f173..bd3ad4f 100644
--- a/app/src/main/java/news/androidtv/tvapprepo/utils/GenerateShortcutHelper.java
+++ b/app/src/main/java/news/androidtv/tvapprepo/utils/GenerateShortcutHelper.java
@@ -6,6 +6,7 @@
import android.content.pm.ResolveInfo;
import android.os.Handler;
import android.os.Looper;
+import android.support.annotation.VisibleForTesting;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.ContextThemeWrapper;
@@ -133,13 +134,19 @@ public static void generateShortcut(final Activity activity, final ResolveInfo r
public static void generateShortcut(final Activity activity, final ResolveInfo resolveInfo,
final AdvancedOptions options) {
+ generateShortcut(activity, resolveInfo, options, null);
+ }
+
+ @VisibleForTesting
+ public static void generateShortcut(final Activity activity, final ResolveInfo resolveInfo,
+ final AdvancedOptions options, final Callback callback) {
if (!options.isReady()) {
// Delay until we complete all web operations
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
Log.d(TAG, "Delaying until web ops are complete");
- generateShortcut(activity, resolveInfo, options);
+ generateShortcut(activity, resolveInfo, options, callback);
}
}, 200);
return;
@@ -157,7 +164,11 @@ public void run() {
new ShortcutPostTask.Callback() {
@Override
public void onResponse(NetworkResponse response) {
- downloadShortcutApk(activity, response, resolveInfo);
+ if (callback != null) {
+ callback.onResponseComplete(new String(response.data));
+ } else {
+ downloadShortcutApk(activity, response, resolveInfo);
+ }
}
@Override
@@ -165,6 +176,12 @@ public void onError(VolleyError error) {
Toast.makeText(activity,
activity.getString(R.string.err_build_failed_reason, error.getMessage()),
Toast.LENGTH_SHORT).show();
+ Toast.makeText(activity,
+ new String(error.networkResponse.data),
+ Toast.LENGTH_LONG).show();
+ if (callback != null) {
+ callback.onResponseFailed(error);
+ }
}
});
}
@@ -237,4 +254,10 @@ public void onRewardedVideoAdFailedToLoad(int i) {
});
ad.loadAd(activity.getString(R.string.reward_video_ad_unit_id), new AdRequest.Builder().build());
}
+
+ @VisibleForTesting
+ public interface Callback {
+ void onResponseComplete(String response);
+ void onResponseFailed(VolleyError error);
+ }
}
diff --git a/app/src/main/java/news/androidtv/tvapprepo/utils/ShortcutPostTask.java b/app/src/main/java/news/androidtv/tvapprepo/utils/ShortcutPostTask.java
index bb03204..770e5cc 100644
--- a/app/src/main/java/news/androidtv/tvapprepo/utils/ShortcutPostTask.java
+++ b/app/src/main/java/news/androidtv/tvapprepo/utils/ShortcutPostTask.java
@@ -45,6 +45,8 @@
public class ShortcutPostTask {
private static final String TAG = ShortcutPostTask.class.getSimpleName();
+ private static final int TIMEOUT = 90 * 1000; // 90 seconds
+
private static final String SUBMISSION_URL =
"http://atvlauncher.trekgonewild.de/index_tvapprepo.php";
private static final String FORM_APP_NAME = "app_name";
@@ -142,7 +144,7 @@ protected Map getByteData() {
} catch (AuthFailureError authFailureError) {
authFailureError.printStackTrace();
}
- sr.setRetryPolicy(new DefaultRetryPolicy(60 * 1000, 1, 0));
+ sr.setRetryPolicy(new DefaultRetryPolicy(TIMEOUT, 1, 0));
queue.add(sr);
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c76b76c..0e0629b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -79,5 +79,5 @@
File Link
Error - Volley cannot get file data for app icon
Cannot find custom banner packs for non-apps
-
+