diff --git a/.gitignore b/.gitignore
index 89f8fd8a18..aa02d7f7c9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,4 +34,6 @@ Thumbs.db
build/
#NDK
-obj/
\ No newline at end of file
+obj/
+
+maka-vlc/
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 6d85aa18df..62cc34b1db 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -12,8 +12,8 @@ android {
applicationId "org.stepic.droid"
minSdkVersion 14
targetSdkVersion 23
- versionCode 32
- versionName "1.4"
+ versionCode 41
+ versionName "1.5.3"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
// Enabling multidex support.
multiDexEnabled true
@@ -25,6 +25,7 @@ android {
'proguard-rules.pro'
}
debug {
+ versionNameSuffix "DEV"
}
}
@@ -40,6 +41,26 @@ android {
main.java.srcDirs += 'src/main/kotlin'
}
+ splits {
+ abi {
+ enable true
+ reset()
+ include 'armeabi', 'armeabi-v7a', 'mips', 'x86' //select ABIs to build APKs for
+ universalApk true //generate an additional APK that contains all the ABIs
+ }
+ }
+
+ // map for the version code
+ project.ext.versionCodes = ['armeabi': 1, 'armeabi-v7a': 2, 'mips': 5, 'x86': 8]
+
+ android.applicationVariants.all { variant ->
+ // assign different version code for each output
+ variant.outputs.each { output ->
+ output.versionCodeOverride =
+ project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 0) + android.defaultConfig.versionCode*10
+ }
+ }
+
packagingOptions {
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
@@ -60,9 +81,6 @@ dependencies {
compile 'com.android.support:multidex:1.0.1'
compile 'com.google.code.gson:gson:2.4'
-
- compile 'com.squareup.okhttp3:okhttp:3.1.2'
- compile 'com.squareup.okhttp:okhttp-ws:2.7.4'
compile 'com.squareup.okio:okio:1.6.0'
compile 'de.hdodenhof:circleimageview:1.3.0'
@@ -108,8 +126,9 @@ dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta1' // or 1.3.1
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1' // or 1.3.1
- testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
- compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" // or 1.3.1
+ testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1' // or 1.3.1
+
+ compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile files('libs/commons-logging-api-1.1.1.jar')
@@ -117,6 +136,7 @@ dependencies {
compile files('libs/log4j-api-2.4.1.jar')
compile files('libs/slf4j-api-1.7.12.jar')
+ compile 'de.mrmaffen:vlc-android-sdk:3.0.0'
}
repositories {
@@ -130,6 +150,4 @@ buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
-}
-
-
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b2b64e640b..65c9073e77 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -11,9 +11,11 @@
+
-
+
@@ -129,6 +131,13 @@
android:value=".view.activities.MainFeedActivity"/>
+
+
diff --git a/app/src/main/java/org/stepic/droid/base/CoursesDatabaseFragmentBase.java b/app/src/main/java/org/stepic/droid/base/CoursesDatabaseFragmentBase.java
index 43ed880b9e..3927fb3283 100644
--- a/app/src/main/java/org/stepic/droid/base/CoursesDatabaseFragmentBase.java
+++ b/app/src/main/java/org/stepic/droid/base/CoursesDatabaseFragmentBase.java
@@ -15,8 +15,8 @@
import com.yandex.metrica.YandexMetrica;
import org.stepic.droid.R;
-import org.stepic.droid.concurrency.FromDbCoursesTask;
-import org.stepic.droid.concurrency.ToDbCoursesTask;
+import org.stepic.droid.concurrency.tasks.FromDbCoursesTask;
+import org.stepic.droid.concurrency.tasks.ToDbCoursesTask;
import org.stepic.droid.events.courses.FailCoursesDownloadEvent;
import org.stepic.droid.events.courses.FailDropCourseEvent;
import org.stepic.droid.events.courses.FinishingGetCoursesFromDbEvent;
@@ -44,9 +44,8 @@
import retrofit.Retrofit;
public abstract class CoursesDatabaseFragmentBase extends CourseListFragmentBase {
- protected FromDbCoursesTask mDbGetCoursesTask;
protected ToDbCoursesTask mDbSaveCoursesTask;
-
+ protected FromDbCoursesTask mDbFromCoursesTask;
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
@@ -82,19 +81,19 @@ protected void showCourses(List cachedCourses) {
private void saveDataToCache(List courses) {
mDbSaveCoursesTask = new ToDbCoursesTask(courses, getCourseType(), mCurrentPage);
- mDbSaveCoursesTask.execute();
+ mDbSaveCoursesTask.executeOnExecutor(mThreadPoolExecutor);
}
public void getAndShowDataFromCache() {
- mDbGetCoursesTask = new FromDbCoursesTask(getCourseType()) {
+ mDbFromCoursesTask = new FromDbCoursesTask(getCourseType()){
@Override
protected void onSuccess(List courses) {
super.onSuccess(courses);
bus.post(new GettingCoursesFromDbSuccessEvent(getCourseType(), courses));
}
};
- mDbGetCoursesTask.execute();
+ mDbFromCoursesTask.executeOnExecutor(mThreadPoolExecutor);
}
@Subscribe
diff --git a/app/src/main/java/org/stepic/droid/base/FragmentActivityBase.java b/app/src/main/java/org/stepic/droid/base/FragmentActivityBase.java
index c8b1d20db6..7ee2769bf1 100644
--- a/app/src/main/java/org/stepic/droid/base/FragmentActivityBase.java
+++ b/app/src/main/java/org/stepic/droid/base/FragmentActivityBase.java
@@ -19,6 +19,8 @@
import org.stepic.droid.util.resolvers.CoursePropertyResolver;
import org.stepic.droid.util.resolvers.IStepResolver;
+import java.util.concurrent.ThreadPoolExecutor;
+
import javax.inject.Inject;
import butterknife.ButterKnife;
@@ -49,6 +51,9 @@ public abstract class FragmentActivityBase extends AppCompatActivity {
@Inject
protected ILoginManager mLoginManager;
+ @Inject
+ protected ThreadPoolExecutor mThreadPoolExecutor;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -99,7 +104,7 @@ protected void onDestroy() {
protected void setFragment(@IdRes int res, Fragment fragment) {
android.support.v4.app.FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
- fragmentTransaction.replace(res, fragment);
+ fragmentTransaction.replace(res, fragment, fragment.getClass().toString());
fragmentTransaction.commit();
}
}
diff --git a/app/src/main/java/org/stepic/droid/base/FragmentBase.java b/app/src/main/java/org/stepic/droid/base/FragmentBase.java
index b8e3c07d05..47e1e585b6 100644
--- a/app/src/main/java/org/stepic/droid/base/FragmentBase.java
+++ b/app/src/main/java/org/stepic/droid/base/FragmentBase.java
@@ -1,5 +1,6 @@
package org.stepic.droid.base;
+import android.app.DownloadManager;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
@@ -10,8 +11,10 @@
import com.squareup.leakcanary.RefWatcher;
import com.squareup.otto.Bus;
+import org.stepic.droid.core.AudioFocusHelper;
import org.stepic.droid.core.ILessonSessionManager;
import org.stepic.droid.core.ILocalProgressManager;
+import org.stepic.droid.concurrency.IMainHandler;
import org.stepic.droid.core.IShell;
import org.stepic.droid.preferences.SharedPreferenceHelper;
import org.stepic.droid.preferences.UserPreferences;
@@ -22,13 +25,18 @@
import org.stepic.droid.util.resolvers.IStepResolver;
import org.stepic.droid.util.resolvers.IVideoResolver;
+import java.util.concurrent.ThreadPoolExecutor;
+
import javax.inject.Inject;
import butterknife.ButterKnife;
public class FragmentBase extends Fragment {
- protected String TAG = "StepicFragment";
+// protected String TAG = "StepicFragment";
+
+ @Inject
+ public ThreadPoolExecutor mThreadPoolExecutor;
@Inject
public ILessonSessionManager mLessonManager;
@@ -69,6 +77,14 @@ public class FragmentBase extends Fragment {
@Inject
public IStepResolver mStepResolver;
+ @Inject
+ public IMainHandler mMainHandler;
+
+ @Inject
+ public AudioFocusHelper mAudioFocusHelper;
+
+ @Inject
+ public DownloadManager mSystemDownloadManager;
public FragmentBase() {
MainApplication.component(MainApplication.getAppContext()).inject(this);
diff --git a/app/src/main/java/org/stepic/droid/base/SingleFragmentActivity.kt b/app/src/main/java/org/stepic/droid/base/SingleFragmentActivity.kt
index f90ab17a8c..a9f4788b93 100644
--- a/app/src/main/java/org/stepic/droid/base/SingleFragmentActivity.kt
+++ b/app/src/main/java/org/stepic/droid/base/SingleFragmentActivity.kt
@@ -6,7 +6,7 @@ import android.support.v4.app.Fragment
import org.stepic.droid.R
abstract class SingleFragmentActivity : FragmentActivityBase() {
- protected abstract fun createFragment(): Fragment
+ protected abstract fun createFragment(): Fragment?
open fun getLayoutResId() = R.layout.activity_fragment
override fun onCreate(savedInstanceState: Bundle?) {
diff --git a/app/src/main/java/org/stepic/droid/concurrency/HandlerBaseDelegate.kt b/app/src/main/java/org/stepic/droid/concurrency/HandlerBaseDelegate.kt
new file mode 100644
index 0000000000..593112373a
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/concurrency/HandlerBaseDelegate.kt
@@ -0,0 +1,8 @@
+package org.stepic.droid.concurrency
+
+import android.os.Handler
+
+abstract class HandlerBaseDelegate : IHandler {
+ override fun post(body: () -> Unit) = getHandler().post { body.invoke() }
+ abstract fun getHandler() : Handler
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/concurrency/IHandler.kt b/app/src/main/java/org/stepic/droid/concurrency/IHandler.kt
new file mode 100644
index 0000000000..1d6ab4b7cf
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/concurrency/IHandler.kt
@@ -0,0 +1,5 @@
+package org.stepic.droid.concurrency
+
+interface IHandler {
+ fun post(body : ()->Unit): Boolean
+}
diff --git a/app/src/main/java/org/stepic/droid/concurrency/IMainHandler.kt b/app/src/main/java/org/stepic/droid/concurrency/IMainHandler.kt
new file mode 100644
index 0000000000..420c0cbeb4
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/concurrency/IMainHandler.kt
@@ -0,0 +1,3 @@
+package org.stepic.droid.concurrency
+
+interface IMainHandler : IHandler
diff --git a/app/src/main/java/org/stepic/droid/concurrency/MainHandlerImpl.kt b/app/src/main/java/org/stepic/droid/concurrency/MainHandlerImpl.kt
new file mode 100644
index 0000000000..b9b196f6d3
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/concurrency/MainHandlerImpl.kt
@@ -0,0 +1,10 @@
+package org.stepic.droid.concurrency
+
+import android.os.Handler
+import org.stepic.droid.base.MainApplication
+
+class MainHandlerImpl : HandlerBaseDelegate(), IMainHandler {
+ val mainHandler = Handler(MainApplication.getAppContext().mainLooper)
+
+ override fun getHandler() = mainHandler
+}
diff --git a/app/src/main/java/org/stepic/droid/concurrency/ToDbCachedVideo.java b/app/src/main/java/org/stepic/droid/concurrency/ToDbCachedVideo.java
deleted file mode 100644
index fe46256207..0000000000
--- a/app/src/main/java/org/stepic/droid/concurrency/ToDbCachedVideo.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.stepic.droid.concurrency;
-
-import android.content.Context;
-
-import org.stepic.droid.base.MainApplication;
-import org.stepic.droid.model.CachedVideo;
-import org.stepic.droid.store.operations.DatabaseFacade;
-
-import javax.inject.Inject;
-
-public class ToDbCachedVideo extends StepicTask {
-
- @Inject
- DatabaseFacade mDatabaseFacade;
-
- private CachedVideo cachedVideo;
-
- public ToDbCachedVideo(CachedVideo cachedVideo) {
- this(MainApplication.getAppContext(), cachedVideo);
- }
-
- public ToDbCachedVideo(Context context, CachedVideo cachedVideo) {
- super(context);
- MainApplication.component(context).inject(this);
- this.cachedVideo = cachedVideo;
- }
-
- @Override
- protected Void doInBackgroundBody(Void... params) throws Exception {
- mDatabaseFacade.addVideo(cachedVideo);
- return null;
- }
-}
diff --git a/app/src/main/java/org/stepic/droid/concurrency/ToDbStepTask.java b/app/src/main/java/org/stepic/droid/concurrency/ToDbStepTask.java
deleted file mode 100644
index ca4b417b24..0000000000
--- a/app/src/main/java/org/stepic/droid/concurrency/ToDbStepTask.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package org.stepic.droid.concurrency;
-
-import com.squareup.otto.Bus;
-
-import org.stepic.droid.base.MainApplication;
-import org.stepic.droid.events.steps.SuccessToDbStepEvent;
-import org.stepic.droid.model.Lesson;
-import org.stepic.droid.model.Step;
-import org.stepic.droid.store.operations.DatabaseFacade;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.inject.Inject;
-
-public class ToDbStepTask extends StepicTask {
- private final List mStepList;
- private final Lesson mLesson;
- @Inject
- DatabaseFacade mDatabaseFacade;
-
- @Inject
- Bus mBus;
-
- public ToDbStepTask(Lesson parentLesson, List steps) {
- super(MainApplication.getAppContext());
-
- MainApplication.component().inject(this);
- mStepList = steps;
- mLesson = parentLesson;
-
- }
-
- public ToDbStepTask(Step oneStep) {
- super(MainApplication.getAppContext());
- MainApplication.component().inject(this);
- mStepList = new ArrayList<>();
- mStepList.add(oneStep);
- mLesson = null;
-
- }
-
- @Override
- protected Void doInBackgroundBody(Void... params) throws Exception {
- for (Step step : mStepList) {
- mDatabaseFacade.addStep(step);
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(AsyncResultWrapper voidAsyncResultWrapper) {
- super.onPostExecute(voidAsyncResultWrapper);
- mBus.post(new SuccessToDbStepEvent(mLesson));
- }
-}
diff --git a/app/src/main/java/org/stepic/droid/concurrency/AsyncResultWrapper.java b/app/src/main/java/org/stepic/droid/concurrency/tasks/AsyncResultWrapper.java
similarity index 90%
rename from app/src/main/java/org/stepic/droid/concurrency/AsyncResultWrapper.java
rename to app/src/main/java/org/stepic/droid/concurrency/tasks/AsyncResultWrapper.java
index 3505eed2d5..f083172b65 100644
--- a/app/src/main/java/org/stepic/droid/concurrency/AsyncResultWrapper.java
+++ b/app/src/main/java/org/stepic/droid/concurrency/tasks/AsyncResultWrapper.java
@@ -1,4 +1,4 @@
-package org.stepic.droid.concurrency;
+package org.stepic.droid.concurrency.tasks;
public class AsyncResultWrapper {
private T result;
diff --git a/app/src/main/java/org/stepic/droid/concurrency/FromDbCoursesTask.java b/app/src/main/java/org/stepic/droid/concurrency/tasks/FromDbCoursesTask.java
similarity index 92%
rename from app/src/main/java/org/stepic/droid/concurrency/FromDbCoursesTask.java
rename to app/src/main/java/org/stepic/droid/concurrency/tasks/FromDbCoursesTask.java
index ef0e3b2e3e..0fe244ddfe 100644
--- a/app/src/main/java/org/stepic/droid/concurrency/FromDbCoursesTask.java
+++ b/app/src/main/java/org/stepic/droid/concurrency/tasks/FromDbCoursesTask.java
@@ -1,10 +1,9 @@
-package org.stepic.droid.concurrency;
+package org.stepic.droid.concurrency.tasks;
import com.squareup.otto.Bus;
import org.jetbrains.annotations.NotNull;
import org.stepic.droid.base.MainApplication;
-import org.stepic.droid.core.IShell;
import org.stepic.droid.events.courses.FinishingGetCoursesFromDbEvent;
import org.stepic.droid.events.courses.StartingGetCoursesFromDbEvent;
import org.stepic.droid.model.Course;
@@ -16,9 +15,6 @@
public class FromDbCoursesTask extends StepicTask> {
- @Inject
- IShell mShell;
-
@Inject
Bus mBus;
@@ -51,4 +47,4 @@ protected void onPostExecute(AsyncResultWrapper> listAsyncResultWra
super.onPostExecute(listAsyncResultWrapper);
mBus.post(new FinishingGetCoursesFromDbEvent(mCourseType, listAsyncResultWrapper.getResult()));
}
-}
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/concurrency/FromDbSectionTask.java b/app/src/main/java/org/stepic/droid/concurrency/tasks/FromDbSectionTask.java
similarity index 97%
rename from app/src/main/java/org/stepic/droid/concurrency/FromDbSectionTask.java
rename to app/src/main/java/org/stepic/droid/concurrency/tasks/FromDbSectionTask.java
index 83eb9c1b59..d25b4f5f19 100644
--- a/app/src/main/java/org/stepic/droid/concurrency/FromDbSectionTask.java
+++ b/app/src/main/java/org/stepic/droid/concurrency/tasks/FromDbSectionTask.java
@@ -1,4 +1,4 @@
-package org.stepic.droid.concurrency;
+package org.stepic.droid.concurrency.tasks;
import com.squareup.otto.Bus;
diff --git a/app/src/main/java/org/stepic/droid/concurrency/FromDbStepTask.java b/app/src/main/java/org/stepic/droid/concurrency/tasks/FromDbStepTask.java
similarity index 97%
rename from app/src/main/java/org/stepic/droid/concurrency/FromDbStepTask.java
rename to app/src/main/java/org/stepic/droid/concurrency/tasks/FromDbStepTask.java
index 917df143db..13cd9e9817 100644
--- a/app/src/main/java/org/stepic/droid/concurrency/FromDbStepTask.java
+++ b/app/src/main/java/org/stepic/droid/concurrency/tasks/FromDbStepTask.java
@@ -1,4 +1,4 @@
-package org.stepic.droid.concurrency;
+package org.stepic.droid.concurrency.tasks;
import com.squareup.otto.Bus;
diff --git a/app/src/main/java/org/stepic/droid/concurrency/FromDbUnitLessonTask.java b/app/src/main/java/org/stepic/droid/concurrency/tasks/FromDbUnitLessonTask.java
similarity index 98%
rename from app/src/main/java/org/stepic/droid/concurrency/FromDbUnitLessonTask.java
rename to app/src/main/java/org/stepic/droid/concurrency/tasks/FromDbUnitLessonTask.java
index c69865150a..a8e9c7fd2c 100644
--- a/app/src/main/java/org/stepic/droid/concurrency/FromDbUnitLessonTask.java
+++ b/app/src/main/java/org/stepic/droid/concurrency/tasks/FromDbUnitLessonTask.java
@@ -1,4 +1,4 @@
-package org.stepic.droid.concurrency;
+package org.stepic.droid.concurrency.tasks;
import com.squareup.otto.Bus;
diff --git a/app/src/main/java/org/stepic/droid/concurrency/StepicTask.java b/app/src/main/java/org/stepic/droid/concurrency/tasks/StepicTask.java
similarity index 98%
rename from app/src/main/java/org/stepic/droid/concurrency/StepicTask.java
rename to app/src/main/java/org/stepic/droid/concurrency/tasks/StepicTask.java
index c880d5e1e2..be321cac4a 100644
--- a/app/src/main/java/org/stepic/droid/concurrency/StepicTask.java
+++ b/app/src/main/java/org/stepic/droid/concurrency/tasks/StepicTask.java
@@ -1,4 +1,4 @@
-package org.stepic.droid.concurrency;
+package org.stepic.droid.concurrency.tasks;
import android.content.Context;
import android.os.AsyncTask;
diff --git a/app/src/main/java/org/stepic/droid/concurrency/ToDbCoursesTask.java b/app/src/main/java/org/stepic/droid/concurrency/tasks/ToDbCoursesTask.java
similarity index 81%
rename from app/src/main/java/org/stepic/droid/concurrency/ToDbCoursesTask.java
rename to app/src/main/java/org/stepic/droid/concurrency/tasks/ToDbCoursesTask.java
index 7f2d941315..3eea9baa07 100644
--- a/app/src/main/java/org/stepic/droid/concurrency/ToDbCoursesTask.java
+++ b/app/src/main/java/org/stepic/droid/concurrency/tasks/ToDbCoursesTask.java
@@ -1,4 +1,4 @@
-package org.stepic.droid.concurrency;
+package org.stepic.droid.concurrency.tasks;
import com.squareup.otto.Bus;
@@ -9,7 +9,6 @@
import org.stepic.droid.model.Course;
import org.stepic.droid.store.operations.DatabaseFacade;
-import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
@@ -37,17 +36,6 @@ public ToDbCoursesTask(List courses, DatabaseFacade.Table type, int page
mCourses = courses;
}
- public ToDbCoursesTask(Course course, DatabaseFacade.Table type) {
- super(MainApplication.getAppContext());
- MainApplication.component().inject(this);
-
- //courses now is not thread safe
- mPage = Integer.MAX_VALUE; //neutral value
- mCourseType = type;
- mCourses = new ArrayList<>();
- mCourses.add(course);
- }
-
@Override
protected Void doInBackgroundBody(Void... params) throws Exception {
diff --git a/app/src/main/java/org/stepic/droid/concurrency/ToDbSectionTask.java b/app/src/main/java/org/stepic/droid/concurrency/tasks/ToDbSectionTask.java
similarity index 97%
rename from app/src/main/java/org/stepic/droid/concurrency/ToDbSectionTask.java
rename to app/src/main/java/org/stepic/droid/concurrency/tasks/ToDbSectionTask.java
index 24ed75a287..cbecd3f47b 100644
--- a/app/src/main/java/org/stepic/droid/concurrency/ToDbSectionTask.java
+++ b/app/src/main/java/org/stepic/droid/concurrency/tasks/ToDbSectionTask.java
@@ -1,4 +1,4 @@
-package org.stepic.droid.concurrency;
+package org.stepic.droid.concurrency.tasks;
import com.squareup.otto.Bus;
diff --git a/app/src/main/java/org/stepic/droid/concurrency/ToDbUnitLessonTask.java b/app/src/main/java/org/stepic/droid/concurrency/tasks/ToDbUnitLessonTask.java
similarity index 97%
rename from app/src/main/java/org/stepic/droid/concurrency/ToDbUnitLessonTask.java
rename to app/src/main/java/org/stepic/droid/concurrency/tasks/ToDbUnitLessonTask.java
index ce6fbc862a..83ab2f8a21 100644
--- a/app/src/main/java/org/stepic/droid/concurrency/ToDbUnitLessonTask.java
+++ b/app/src/main/java/org/stepic/droid/concurrency/tasks/ToDbUnitLessonTask.java
@@ -1,4 +1,4 @@
-package org.stepic.droid.concurrency;
+package org.stepic.droid.concurrency.tasks;
import com.squareup.otto.Bus;
diff --git a/app/src/main/java/org/stepic/droid/concurrency/UpdateCourseTask.java b/app/src/main/java/org/stepic/droid/concurrency/tasks/UpdateCourseTask.java
similarity index 96%
rename from app/src/main/java/org/stepic/droid/concurrency/UpdateCourseTask.java
rename to app/src/main/java/org/stepic/droid/concurrency/tasks/UpdateCourseTask.java
index 3428a35b99..49e5f3dc7c 100644
--- a/app/src/main/java/org/stepic/droid/concurrency/UpdateCourseTask.java
+++ b/app/src/main/java/org/stepic/droid/concurrency/tasks/UpdateCourseTask.java
@@ -1,4 +1,4 @@
-package org.stepic.droid.concurrency;
+package org.stepic.droid.concurrency.tasks;
import org.stepic.droid.base.MainApplication;
import org.stepic.droid.core.IShell;
diff --git a/app/src/main/java/org/stepic/droid/core/ActivityFinisher.java b/app/src/main/java/org/stepic/droid/core/ActivityFinisher.java
deleted file mode 100644
index aa64a37d0b..0000000000
--- a/app/src/main/java/org/stepic/droid/core/ActivityFinisher.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package org.stepic.droid.core;
-
-public interface ActivityFinisher {
- void onFinish();
-}
diff --git a/app/src/main/java/org/stepic/droid/core/ActivityFinisher.kt b/app/src/main/java/org/stepic/droid/core/ActivityFinisher.kt
new file mode 100644
index 0000000000..b9bd1f8b3c
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/core/ActivityFinisher.kt
@@ -0,0 +1,5 @@
+package org.stepic.droid.core
+
+interface ActivityFinisher {
+ fun onFinish()
+}
diff --git a/app/src/main/java/org/stepic/droid/core/AudioFocusHelper.kt b/app/src/main/java/org/stepic/droid/core/AudioFocusHelper.kt
new file mode 100644
index 0000000000..ce39210fe8
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/core/AudioFocusHelper.kt
@@ -0,0 +1,30 @@
+package org.stepic.droid.core
+
+import android.content.Context
+import android.media.AudioManager
+import com.squareup.otto.Bus
+import org.stepic.droid.concurrency.IMainHandler
+import org.stepic.droid.events.audio.AudioFocusGainEvent
+import org.stepic.droid.events.audio.AudioFocusLossEvent
+
+class AudioFocusHelper(context: Context, bus: Bus, mainHandler: IMainHandler) : AudioManager.OnAudioFocusChangeListener {
+ val mAudioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
+ val mContext = context
+ val mBus = bus
+ val mMainHandler = mainHandler
+
+ fun requestAudioFocus() = AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
+ mAudioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN)
+
+
+ fun releaseAudioFocus() = AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
+ mAudioManager.abandonAudioFocus(this);
+
+
+ override fun onAudioFocusChange(focusChange: Int) {
+ when (focusChange) {
+ AudioManager.AUDIOFOCUS_GAIN -> mMainHandler.post { mBus.post(AudioFocusGainEvent()) }
+ AudioManager.AUDIOFOCUS_LOSS -> mMainHandler.post { mBus.post(AudioFocusLossEvent()) }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/core/ILessonSessionManager.java b/app/src/main/java/org/stepic/droid/core/ILessonSessionManager.java
deleted file mode 100644
index e40a219475..0000000000
--- a/app/src/main/java/org/stepic/droid/core/ILessonSessionManager.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.stepic.droid.core;
-
-import org.jetbrains.annotations.Nullable;
-import org.stepic.droid.model.Attempt;
-import org.stepic.droid.model.Submission;
-
-public interface ILessonSessionManager {
- @Nullable
- Submission restoreSubmissionForStep(long stepId);
-
- @Nullable
- Attempt restoreAttemptForStep(long stepId);
-
- void saveSession(long stepId, @Nullable Attempt attempt, @Nullable Submission submission);
-
- void reset();
-}
diff --git a/app/src/main/java/org/stepic/droid/core/ILessonSessionManager.kt b/app/src/main/java/org/stepic/droid/core/ILessonSessionManager.kt
new file mode 100644
index 0000000000..27cd741bf1
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/core/ILessonSessionManager.kt
@@ -0,0 +1,14 @@
+package org.stepic.droid.core
+
+import org.stepic.droid.model.Attempt
+import org.stepic.droid.model.Submission
+
+interface ILessonSessionManager {
+ fun restoreSubmissionForStep(stepId: Long): Submission?
+
+ fun restoreAttemptForStep(stepId: Long): Attempt?
+
+ fun saveSession(stepId: Long, attempt: Attempt?, submission: Submission?)
+
+ fun reset()
+}
diff --git a/app/src/main/java/org/stepic/droid/core/IScreenManager.java b/app/src/main/java/org/stepic/droid/core/IScreenManager.java
index 76915e8bbf..6d6b8181ed 100644
--- a/app/src/main/java/org/stepic/droid/core/IScreenManager.java
+++ b/app/src/main/java/org/stepic/droid/core/IScreenManager.java
@@ -32,15 +32,10 @@ public interface IScreenManager {
void openStepInWeb(Context context, Step step);
- void openSignUpInWeb(Context context);
-
void openRemindPassword(AppCompatActivity context);
void pushToViewedQueue(ViewAssignment viewAssignmentWrapper);
- @Deprecated
- void showSocialLogin(Context context);
-
void showCourseDescription(Activity sourceActivity, @NotNull Course course);
void showTextFeedback(Activity sourceActivity);
@@ -48,4 +43,6 @@ public interface IScreenManager {
void showStoreWithApp(Activity sourceActivity);
void showDownload();
+
+ void showVideo(Activity sourceActivity, String source);
}
diff --git a/app/src/main/java/org/stepic/droid/core/MyStatePhoneListener.kt b/app/src/main/java/org/stepic/droid/core/MyStatePhoneListener.kt
new file mode 100644
index 0000000000..3de1005523
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/core/MyStatePhoneListener.kt
@@ -0,0 +1,27 @@
+package org.stepic.droid.core
+
+import android.telephony.PhoneStateListener
+import com.squareup.otto.Bus
+import org.stepic.droid.base.MainApplication
+import org.stepic.droid.concurrency.IMainHandler
+import org.stepic.droid.events.IncomingCallEvent
+import javax.inject.Inject
+
+class MyStatePhoneListener: PhoneStateListener() {
+
+ init {
+ MainApplication.component().inject(this)
+ }
+
+ @Inject
+ lateinit var mBus: Bus
+
+ @Inject
+ lateinit var mHandler: IMainHandler
+
+ override fun onCallStateChanged(state: Int, incomingNumber: String?) {
+ if (state == 1) {
+ mHandler.post { mBus.post(IncomingCallEvent()) }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/core/ScreenManager.java b/app/src/main/java/org/stepic/droid/core/ScreenManager.java
index 0bd58d6abe..9ead51d3a7 100644
--- a/app/src/main/java/org/stepic/droid/core/ScreenManager.java
+++ b/app/src/main/java/org/stepic/droid/core/ScreenManager.java
@@ -8,10 +8,12 @@
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
+import android.widget.Toast;
import com.yandex.metrica.YandexMetrica;
import org.jetbrains.annotations.NotNull;
+import org.stepic.droid.R;
import org.stepic.droid.base.MainApplication;
import org.stepic.droid.configuration.IConfig;
import org.stepic.droid.model.Course;
@@ -19,6 +21,7 @@
import org.stepic.droid.model.Section;
import org.stepic.droid.model.Step;
import org.stepic.droid.model.Unit;
+import org.stepic.droid.preferences.UserPreferences;
import org.stepic.droid.services.ViewPusher;
import org.stepic.droid.util.AppConstants;
import org.stepic.droid.util.JsonHelper;
@@ -32,9 +35,11 @@
import org.stepic.droid.view.activities.StepsActivity;
import org.stepic.droid.view.activities.TextFeedbackActivity;
import org.stepic.droid.view.activities.UnitsActivity;
+import org.stepic.droid.view.activities.VideoActivity;
import org.stepic.droid.view.dialogs.RemindPasswordDialogFragment;
import org.stepic.droid.view.fragments.DownloadsFragment;
import org.stepic.droid.web.ViewAssignment;
+import org.videolan.libvlc.util.VLCUtil;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -43,12 +48,13 @@
public class ScreenManager implements IScreenManager {
private IConfig mConfig;
private IMainMenuResolver mMainMenuResolver;
-
+ private UserPreferences mUserPreferences;
@Inject
- public ScreenManager(IConfig config, IMainMenuResolver mainMenuResolver) {
+ public ScreenManager(IConfig config, IMainMenuResolver mainMenuResolver, UserPreferences userPreferences) {
this.mConfig = config;
mMainMenuResolver = mainMenuResolver;
+ mUserPreferences = userPreferences;
}
@Override
@@ -134,8 +140,8 @@ public void showStoreWithApp(@NotNull Activity sourceActivity) {
@Override
public void showDownload() {
- Intent intent = new Intent (MainApplication.getAppContext(), MainFeedActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Intent intent = new Intent(MainApplication.getAppContext(), MainFeedActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
Bundle bundle = new Bundle();
int index = mMainMenuResolver.getIndexOfFragment(DownloadsFragment.class);
bundle.putInt(MainFeedActivity.KEY_CURRENT_INDEX, index);
@@ -143,6 +149,41 @@ public void showDownload() {
MainApplication.getAppContext().startActivity(intent);
}
+ @Override
+ public void showVideo(Activity sourceActivity, String videoPath) {
+ YandexMetrica.reportEvent("video is tried to show");
+ boolean isOpenExternal = mUserPreferences.isOpenInExternal();
+ if (isOpenExternal){
+ YandexMetrica.reportEvent("video open external");
+ }
+ else{
+ YandexMetrica.reportEvent("video open native");
+ }
+
+ boolean isCompatible = VLCUtil.hasCompatibleCPU(MainApplication.getAppContext());
+ if (!isCompatible){
+ YandexMetrica.reportEvent("video is not compatible");
+ }
+
+
+
+ if (isCompatible && !isOpenExternal) {
+ Intent intent = new Intent(MainApplication.getAppContext(), VideoActivity.class);
+ intent.putExtra(VideoActivity.Companion.getVideoPathKey(), videoPath);
+ sourceActivity.startActivity(intent);
+ } else {
+ Uri videoUri = Uri.parse(videoPath);
+ Intent intent = new Intent(Intent.ACTION_VIEW, videoUri);
+ intent.setDataAndType(videoUri, "video/*");
+ try {
+ sourceActivity.startActivity(intent);
+ } catch (Exception ex) {
+ YandexMetrica.reportError("NotPlayer", ex);
+ Toast.makeText(sourceActivity, R.string.not_video_player_error, Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+
@Override
public void showSections(Context sourceActivity, @NotNull Course course) {
YandexMetrica.reportEvent("Screen manager: show section", JsonHelper.toJson(course));
@@ -183,14 +224,6 @@ public void openStepInWeb(Context context, Step step) {
context.startActivity(intent);
}
- @Override
- public void openSignUpInWeb(Context context) {
- YandexMetrica.reportEvent("Screen manager: open signup in Web");
- String url = mConfig.getBaseUrl() + "/accounts/signup/";
- final Intent intent = new Intent(Intent.ACTION_VIEW).setData(Uri.parse(url));
- context.startActivity(intent);
- }
-
@Override
public void openRemindPassword(AppCompatActivity context) {
YandexMetrica.reportEvent("Screen manager: remind password");
@@ -208,11 +241,4 @@ public void pushToViewedQueue(ViewAssignment viewAssignmentWrapper) {
MainApplication.getAppContext().startService(loadIntent);
}
- @Override
- public void showSocialLogin(Context context) {
- String url = mConfig.getBaseUrl() + "/oauth2/authorize/?client_id=P3svssuGYOJ8g8rrJSJtVbqnyE0QinTfncbfFr9p&response_type=token";
- final Intent intent = new Intent(Intent.ACTION_VIEW).setData(Uri.parse(url));
- context.startActivity(intent);
- }
-
}
diff --git a/app/src/main/java/org/stepic/droid/core/StepicCoreComponent.java b/app/src/main/java/org/stepic/droid/core/StepicCoreComponent.java
index ee7828d9d6..e1804756e6 100644
--- a/app/src/main/java/org/stepic/droid/core/StepicCoreComponent.java
+++ b/app/src/main/java/org/stepic/droid/core/StepicCoreComponent.java
@@ -2,16 +2,14 @@
import org.stepic.droid.base.FragmentActivityBase;
import org.stepic.droid.base.FragmentBase;
-import org.stepic.droid.concurrency.FromDbCoursesTask;
-import org.stepic.droid.concurrency.FromDbSectionTask;
-import org.stepic.droid.concurrency.FromDbStepTask;
-import org.stepic.droid.concurrency.FromDbUnitLessonTask;
-import org.stepic.droid.concurrency.ToDbCachedVideo;
-import org.stepic.droid.concurrency.ToDbCoursesTask;
-import org.stepic.droid.concurrency.ToDbSectionTask;
-import org.stepic.droid.concurrency.ToDbStepTask;
-import org.stepic.droid.concurrency.ToDbUnitLessonTask;
-import org.stepic.droid.concurrency.UpdateCourseTask;
+import org.stepic.droid.concurrency.tasks.FromDbCoursesTask;
+import org.stepic.droid.concurrency.tasks.FromDbSectionTask;
+import org.stepic.droid.concurrency.tasks.FromDbStepTask;
+import org.stepic.droid.concurrency.tasks.FromDbUnitLessonTask;
+import org.stepic.droid.concurrency.tasks.ToDbCoursesTask;
+import org.stepic.droid.concurrency.tasks.ToDbSectionTask;
+import org.stepic.droid.concurrency.tasks.ToDbUnitLessonTask;
+import org.stepic.droid.concurrency.tasks.UpdateCourseTask;
import org.stepic.droid.model.Course;
import org.stepic.droid.model.Section;
import org.stepic.droid.receivers.DownloadClickReceiver;
@@ -36,7 +34,6 @@
import org.stepic.droid.view.dialogs.RemindPasswordDialogFragment;
import org.stepic.droid.view.dialogs.VideoQualityDialog;
import org.stepic.droid.view.fragments.DownloadsFragment;
-import org.stepic.droid.web.HttpManager;
import org.stepic.droid.web.RetrofitRESTApi;
import javax.inject.Singleton;
@@ -50,8 +47,6 @@ public interface StepicCoreComponent {
void inject(Shell injectAllToShell);
- void inject(HttpManager httpManager);
-
void inject(MyCoursesAdapter adapter);
void inject(Course adapter);
@@ -77,8 +72,6 @@ public interface StepicCoreComponent {
//All Tasks:
- void inject(FromDbCoursesTask stepicTask);
-
void inject(ToDbCoursesTask stepicTask);
void inject(UpdateCourseTask stepicTask);
@@ -91,12 +84,8 @@ public interface StepicCoreComponent {
void inject(ToDbUnitLessonTask stepicTask);
- void inject(ToDbStepTask stepicTask);
-
void inject(FromDbStepTask stepicTask);
- void inject(ToDbCachedVideo stepicTask);
-
void inject(AllowMobileDataDialogFragment allowMobileDataDialogFragment);
void inject(LoadService loadService);
@@ -126,4 +115,8 @@ public interface StepicCoreComponent {
void inject(CancelLoadingService service);
void inject(DownloadClickReceiver downloadClickReceiver);
+
+ void inject(FromDbCoursesTask fromDbCoursesTask);
+
+ void inject(MyStatePhoneListener listener);
}
diff --git a/app/src/main/java/org/stepic/droid/core/StepicDefaultModule.java b/app/src/main/java/org/stepic/droid/core/StepicDefaultModule.java
index e9efaddcef..dac2bd8c42 100644
--- a/app/src/main/java/org/stepic/droid/core/StepicDefaultModule.java
+++ b/app/src/main/java/org/stepic/droid/core/StepicDefaultModule.java
@@ -6,6 +6,8 @@
import com.squareup.otto.Bus;
+import org.stepic.droid.concurrency.IMainHandler;
+import org.stepic.droid.concurrency.MainHandlerImpl;
import org.stepic.droid.configuration.ConfigRelease;
import org.stepic.droid.configuration.IConfig;
import org.stepic.droid.model.Assignment;
@@ -51,12 +53,14 @@
import org.stepic.droid.util.resolvers.SearchResolver;
import org.stepic.droid.util.resolvers.StepTypeResolver;
import org.stepic.droid.util.resolvers.VideoResolver;
-import org.stepic.droid.web.HttpManager;
import org.stepic.droid.web.IApi;
-import org.stepic.droid.web.IHttpManager;
import org.stepic.droid.web.RetrofitRESTApi;
import org.stepic.droid.web.ViewAssignment;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadPoolExecutor;
+
import javax.inject.Singleton;
import dagger.Module;
@@ -74,8 +78,8 @@ public StepicDefaultModule(Context context) {
@Provides
@Singleton
- public IScreenManager provideIScreenManager(IConfig config, IMainMenuResolver mainMenuResolver) {
- return new ScreenManager(config, mainMenuResolver);
+ public IScreenManager provideIScreenManager(IConfig config, IMainMenuResolver mainMenuResolver, UserPreferences userPreferences) {
+ return new ScreenManager(config, mainMenuResolver, userPreferences);
}
@Provides
@@ -97,12 +101,6 @@ public IApi provideIApi() {
return new RetrofitRESTApi();
}
- @Provides
- @Singleton
- public IHttpManager provideIHttpManager(Context context) {
- return new HttpManager(context);
- }
-
@Provides
@Singleton
public SharedPreferenceHelper provideSharedPreferencesHelper() {
@@ -279,8 +277,33 @@ public ICancelSniffer provideCancelSniffer() {
@Provides
@Singleton
- public IMainMenuResolver provideResolver(){
+ public IMainMenuResolver provideResolver() {
return new MainMenuResolverImpl();
}
+ @Provides
+ @Singleton
+ public ExecutorService provideSingle() {
+ return Executors.newSingleThreadExecutor();
+ }
+
+
+ //it is good for many short lived, which should do async
+ @Provides
+ @Singleton
+ public ThreadPoolExecutor provideThreadPool() {
+ return (ThreadPoolExecutor) Executors.newCachedThreadPool();
+ }
+
+ @Singleton
+ @Provides
+ public IMainHandler provideHandlerForUIThread() {
+ return new MainHandlerImpl();
+ }
+
+ @Singleton
+ @Provides
+ public AudioFocusHelper provideAudioFocusHelper(Context context, IMainHandler mainHandler, Bus bus) {
+ return new AudioFocusHelper(context, bus, mainHandler);
+ }
}
diff --git a/app/src/main/java/org/stepic/droid/events/IncomingCallEvent.kt b/app/src/main/java/org/stepic/droid/events/IncomingCallEvent.kt
new file mode 100644
index 0000000000..422b8ab80c
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/events/IncomingCallEvent.kt
@@ -0,0 +1,4 @@
+package org.stepic.droid.events
+
+class IncomingCallEvent {
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/events/InternetIsEnabledEvent.java b/app/src/main/java/org/stepic/droid/events/InternetIsEnabledEvent.java
deleted file mode 100644
index 360ce91516..0000000000
--- a/app/src/main/java/org/stepic/droid/events/InternetIsEnabledEvent.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package org.stepic.droid.events;
-
-public class InternetIsEnabledEvent {
-}
diff --git a/app/src/main/java/org/stepic/droid/events/InternetIsEnabledEvent.kt b/app/src/main/java/org/stepic/droid/events/InternetIsEnabledEvent.kt
new file mode 100644
index 0000000000..f8a97f3eea
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/events/InternetIsEnabledEvent.kt
@@ -0,0 +1,3 @@
+package org.stepic.droid.events
+
+class InternetIsEnabledEvent
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/events/attempts/AttemptBaseEvent.java b/app/src/main/java/org/stepic/droid/events/attempts/AttemptBaseEvent.java
deleted file mode 100644
index 17c12001ac..0000000000
--- a/app/src/main/java/org/stepic/droid/events/attempts/AttemptBaseEvent.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.stepic.droid.events.attempts;
-
-import org.stepic.droid.model.Attempt;
-
-public class AttemptBaseEvent {
-
- private long stepId;
- private Attempt mAttempt;
-
- public AttemptBaseEvent(long stepId, Attempt attempt) {
-
- this.stepId = stepId;
- mAttempt = attempt;
- }
-
- public Attempt getAttempt() {
- return mAttempt;
- }
-
-
- public long getStepId() {
- return stepId;
- }
-}
diff --git a/app/src/main/java/org/stepic/droid/events/attempts/AttemptBaseEvent.kt b/app/src/main/java/org/stepic/droid/events/attempts/AttemptBaseEvent.kt
new file mode 100644
index 0000000000..0e6c5f63e4
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/events/attempts/AttemptBaseEvent.kt
@@ -0,0 +1,5 @@
+package org.stepic.droid.events.attempts
+
+import org.stepic.droid.model.Attempt
+
+open class AttemptBaseEvent(val stepId: Long, val attempt: Attempt?)
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/events/attempts/FailAttemptEvent.java b/app/src/main/java/org/stepic/droid/events/attempts/FailAttemptEvent.java
deleted file mode 100644
index a5f4e929eb..0000000000
--- a/app/src/main/java/org/stepic/droid/events/attempts/FailAttemptEvent.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package org.stepic.droid.events.attempts;
-
-public class FailAttemptEvent extends AttemptBaseEvent {
- public FailAttemptEvent(long stepId) {
- super(stepId, null);
- }
-}
-
diff --git a/app/src/main/java/org/stepic/droid/events/attempts/FailAttemptEvent.kt b/app/src/main/java/org/stepic/droid/events/attempts/FailAttemptEvent.kt
new file mode 100644
index 0000000000..8190b8f620
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/events/attempts/FailAttemptEvent.kt
@@ -0,0 +1,3 @@
+package org.stepic.droid.events.attempts
+
+class FailAttemptEvent(stepId: Long) : AttemptBaseEvent(stepId, null)
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/events/attempts/SuccessAttemptEvent.java b/app/src/main/java/org/stepic/droid/events/attempts/SuccessAttemptEvent.java
deleted file mode 100644
index f2af8ba35d..0000000000
--- a/app/src/main/java/org/stepic/droid/events/attempts/SuccessAttemptEvent.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.stepic.droid.events.attempts;
-
-import org.stepic.droid.model.Attempt;
-
-public class SuccessAttemptEvent extends AttemptBaseEvent {
- private final boolean justCreated;
-
- public SuccessAttemptEvent(long stepId, Attempt attempt, boolean b) {
- super(stepId, attempt);
- justCreated = b;
- }
-
- public boolean isJustCreated() {
- return justCreated;
- }
-}
diff --git a/app/src/main/java/org/stepic/droid/events/attempts/SuccessAttemptEvent.kt b/app/src/main/java/org/stepic/droid/events/attempts/SuccessAttemptEvent.kt
new file mode 100644
index 0000000000..37137d654c
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/events/attempts/SuccessAttemptEvent.kt
@@ -0,0 +1,5 @@
+package org.stepic.droid.events.attempts
+
+import org.stepic.droid.model.Attempt
+
+class SuccessAttemptEvent(stepId: Long, attempt: Attempt?, val isJustCreated: Boolean) : AttemptBaseEvent(stepId, attempt)
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/events/audio/AudioFocusGainEvent.kt b/app/src/main/java/org/stepic/droid/events/audio/AudioFocusGainEvent.kt
new file mode 100644
index 0000000000..cd0aeb66af
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/events/audio/AudioFocusGainEvent.kt
@@ -0,0 +1,3 @@
+package org.stepic.droid.events.audio
+
+class AudioFocusGainEvent
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/events/audio/AudioFocusLossEvent.kt b/app/src/main/java/org/stepic/droid/events/audio/AudioFocusLossEvent.kt
new file mode 100644
index 0000000000..7028362fdb
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/events/audio/AudioFocusLossEvent.kt
@@ -0,0 +1,3 @@
+package org.stepic.droid.events.audio
+
+class AudioFocusLossEvent
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/events/audio/AudioFocusLostDownVolume.kt b/app/src/main/java/org/stepic/droid/events/audio/AudioFocusLostDownVolume.kt
new file mode 100644
index 0000000000..783491e04b
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/events/audio/AudioFocusLostDownVolume.kt
@@ -0,0 +1,3 @@
+package org.stepic.droid.events.audio
+
+class AudioFocusLostDownVolume
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/events/audio/AudioFocusLostShortTimeEvent.kt b/app/src/main/java/org/stepic/droid/events/audio/AudioFocusLostShortTimeEvent.kt
new file mode 100644
index 0000000000..5a486eeea5
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/events/audio/AudioFocusLostShortTimeEvent.kt
@@ -0,0 +1,3 @@
+package org.stepic.droid.events.audio
+
+class AudioFocusLostShortTimeEvent
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/events/feedback/FeedbackFailedEvent.java b/app/src/main/java/org/stepic/droid/events/feedback/FeedbackFailedEvent.java
deleted file mode 100644
index dd3f671614..0000000000
--- a/app/src/main/java/org/stepic/droid/events/feedback/FeedbackFailedEvent.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package org.stepic.droid.events.feedback;
-
-public class FeedbackFailedEvent {
-}
diff --git a/app/src/main/java/org/stepic/droid/events/feedback/FeedbackFailedEvent.kt b/app/src/main/java/org/stepic/droid/events/feedback/FeedbackFailedEvent.kt
new file mode 100644
index 0000000000..06d0125ca4
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/events/feedback/FeedbackFailedEvent.kt
@@ -0,0 +1,3 @@
+package org.stepic.droid.events.feedback
+
+class FeedbackFailedEvent
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/events/feedback/FeedbackInternetProblemsEvent.java b/app/src/main/java/org/stepic/droid/events/feedback/FeedbackInternetProblemsEvent.java
deleted file mode 100644
index 2bf4184920..0000000000
--- a/app/src/main/java/org/stepic/droid/events/feedback/FeedbackInternetProblemsEvent.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package org.stepic.droid.events.feedback;
-
-public class FeedbackInternetProblemsEvent {
-}
diff --git a/app/src/main/java/org/stepic/droid/events/feedback/FeedbackInternetProblemsEvent.kt b/app/src/main/java/org/stepic/droid/events/feedback/FeedbackInternetProblemsEvent.kt
new file mode 100644
index 0000000000..6af029dded
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/events/feedback/FeedbackInternetProblemsEvent.kt
@@ -0,0 +1,3 @@
+package org.stepic.droid.events.feedback
+
+class FeedbackInternetProblemsEvent
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/events/feedback/FeedbackSentEvent.java b/app/src/main/java/org/stepic/droid/events/feedback/FeedbackSentEvent.java
deleted file mode 100644
index 92a6692dc3..0000000000
--- a/app/src/main/java/org/stepic/droid/events/feedback/FeedbackSentEvent.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package org.stepic.droid.events.feedback;
-
-public class FeedbackSentEvent {
-}
diff --git a/app/src/main/java/org/stepic/droid/events/feedback/FeedbackSentEvent.kt b/app/src/main/java/org/stepic/droid/events/feedback/FeedbackSentEvent.kt
new file mode 100644
index 0000000000..48a529bf93
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/events/feedback/FeedbackSentEvent.kt
@@ -0,0 +1,3 @@
+package org.stepic.droid.events.feedback
+
+class FeedbackSentEvent
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/events/joining_course/FailJoinEvent.java b/app/src/main/java/org/stepic/droid/events/joining_course/FailJoinEvent.java
deleted file mode 100644
index 64d35689ff..0000000000
--- a/app/src/main/java/org/stepic/droid/events/joining_course/FailJoinEvent.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.stepic.droid.events.joining_course;
-
-import retrofit.Response;
-
-public class FailJoinEvent {
- private final Response response;
-
- public Response getResponse() {
- return response;
- }
-
- public FailJoinEvent() {
- response = null;
-
- }
-
- public FailJoinEvent(Response response) {
- this.response = response;
- }
-}
diff --git a/app/src/main/java/org/stepic/droid/events/joining_course/FailJoinEvent.kt b/app/src/main/java/org/stepic/droid/events/joining_course/FailJoinEvent.kt
new file mode 100644
index 0000000000..df092970d8
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/events/joining_course/FailJoinEvent.kt
@@ -0,0 +1,5 @@
+package org.stepic.droid.events.joining_course
+
+import retrofit.Response
+
+class FailJoinEvent(val response: Response? = null)
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/events/joining_course/SuccessJoinEvent.java b/app/src/main/java/org/stepic/droid/events/joining_course/SuccessJoinEvent.java
deleted file mode 100644
index 5bbc3c93d6..0000000000
--- a/app/src/main/java/org/stepic/droid/events/joining_course/SuccessJoinEvent.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.stepic.droid.events.joining_course;
-
-import org.stepic.droid.model.Course;
-
-public class SuccessJoinEvent {
- private Course mCourse;
-
- public SuccessJoinEvent(Course mCourse) {
-
- this.mCourse = mCourse;
- }
-
- public Course getCourse() {
- return mCourse;
- }
-}
diff --git a/app/src/main/java/org/stepic/droid/events/joining_course/SuccessJoinEvent.kt b/app/src/main/java/org/stepic/droid/events/joining_course/SuccessJoinEvent.kt
new file mode 100644
index 0000000000..7e62dc832c
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/events/joining_course/SuccessJoinEvent.kt
@@ -0,0 +1,5 @@
+package org.stepic.droid.events.joining_course
+
+import org.stepic.droid.model.Course
+
+class SuccessJoinEvent(val course: Course?)
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/events/profile/ProfileCanBeShownEvent.java b/app/src/main/java/org/stepic/droid/events/profile/ProfileCanBeShownEvent.java
deleted file mode 100644
index 2a5dff767d..0000000000
--- a/app/src/main/java/org/stepic/droid/events/profile/ProfileCanBeShownEvent.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.stepic.droid.events.profile;
-
-import org.stepic.droid.model.Profile;
-
-public class ProfileCanBeShownEvent {
- Profile profile;
-
- public ProfileCanBeShownEvent(Profile profile) {
- this.profile = profile;
- }
-
- public Profile getProfile() {
- return profile;
- }
-}
-
diff --git a/app/src/main/java/org/stepic/droid/events/profile/ProfileCanBeShownEvent.kt b/app/src/main/java/org/stepic/droid/events/profile/ProfileCanBeShownEvent.kt
new file mode 100644
index 0000000000..2838a8559c
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/events/profile/ProfileCanBeShownEvent.kt
@@ -0,0 +1,5 @@
+package org.stepic.droid.events.profile
+
+import org.stepic.droid.model.Profile
+
+class ProfileCanBeShownEvent(var profile: Profile?)
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/events/video/DownloadReportEvent.kt b/app/src/main/java/org/stepic/droid/events/video/DownloadReportEvent.kt
new file mode 100644
index 0000000000..b0727b44ac
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/events/video/DownloadReportEvent.kt
@@ -0,0 +1,5 @@
+package org.stepic.droid.events.video
+
+import org.stepic.droid.model.DownloadReportItem
+
+data class DownloadReportEvent(val downloadReportItem: DownloadReportItem)
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/events/video/VideoCachedOnDiskEvent.kt b/app/src/main/java/org/stepic/droid/events/video/VideoCachedOnDiskEvent.kt
new file mode 100644
index 0000000000..025a5daa93
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/events/video/VideoCachedOnDiskEvent.kt
@@ -0,0 +1,6 @@
+package org.stepic.droid.events.video
+
+import org.stepic.droid.model.CachedVideo
+import org.stepic.droid.model.Lesson
+
+data class VideoCachedOnDiskEvent(val stepId : Long, val lesson: Lesson, val video : CachedVideo)
diff --git a/app/src/main/java/org/stepic/droid/events/wifi_settings/WifiLoadIsChangedEvent.java b/app/src/main/java/org/stepic/droid/events/wifi_settings/WifiLoadIsChangedEvent.java
deleted file mode 100644
index ce7fe5f36d..0000000000
--- a/app/src/main/java/org/stepic/droid/events/wifi_settings/WifiLoadIsChangedEvent.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.stepic.droid.events.wifi_settings;
-
-public class WifiLoadIsChangedEvent {
- final boolean newStateMobileAllowed;
-
- public WifiLoadIsChangedEvent(boolean newStateMobileAllowed) {
- this.newStateMobileAllowed = newStateMobileAllowed;
- }
-
- public boolean isNewStateMobileAllowed() {
- return newStateMobileAllowed;
- }
-}
diff --git a/app/src/main/java/org/stepic/droid/events/wifi_settings/WifiLoadIsChangedEvent.kt b/app/src/main/java/org/stepic/droid/events/wifi_settings/WifiLoadIsChangedEvent.kt
new file mode 100644
index 0000000000..1268e0e99b
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/events/wifi_settings/WifiLoadIsChangedEvent.kt
@@ -0,0 +1,3 @@
+package org.stepic.droid.events.wifi_settings
+
+class WifiLoadIsChangedEvent(val isNewStateMobileAllowed: Boolean)
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/exceptions/AuthException.java b/app/src/main/java/org/stepic/droid/exceptions/AuthException.java
deleted file mode 100644
index 3fea041dcd..0000000000
--- a/app/src/main/java/org/stepic/droid/exceptions/AuthException.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package org.stepic.droid.exceptions;
-
-import java.io.IOException;
-
-public class AuthException extends IOException {
-}
diff --git a/app/src/main/java/org/stepic/droid/exceptions/NullCourseListException.java b/app/src/main/java/org/stepic/droid/exceptions/NullCourseListException.java
deleted file mode 100644
index 6d416e0fb9..0000000000
--- a/app/src/main/java/org/stepic/droid/exceptions/NullCourseListException.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package org.stepic.droid.exceptions;
-
-public class NullCourseListException extends NullPointerException {
-}
diff --git a/app/src/main/java/org/stepic/droid/exceptions/NullProfileException.java b/app/src/main/java/org/stepic/droid/exceptions/NullProfileException.java
deleted file mode 100644
index 619bb94620..0000000000
--- a/app/src/main/java/org/stepic/droid/exceptions/NullProfileException.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package org.stepic.droid.exceptions;
-
-public class NullProfileException extends NullPointerException {
-}
diff --git a/app/src/main/java/org/stepic/droid/exceptions/UnitStoredButLessonNotException.java b/app/src/main/java/org/stepic/droid/exceptions/UnitStoredButLessonNotException.java
deleted file mode 100644
index 9291afdaeb..0000000000
--- a/app/src/main/java/org/stepic/droid/exceptions/UnitStoredButLessonNotException.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package org.stepic.droid.exceptions;
-
-public class UnitStoredButLessonNotException extends Exception {
-}
diff --git a/app/src/main/java/org/stepic/droid/exceptions/UnitStoredButLessonNotException.kt b/app/src/main/java/org/stepic/droid/exceptions/UnitStoredButLessonNotException.kt
new file mode 100644
index 0000000000..ec56b58441
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/exceptions/UnitStoredButLessonNotException.kt
@@ -0,0 +1,3 @@
+package org.stepic.droid.exceptions
+
+class UnitStoredButLessonNotException : Exception()
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/model/DownloadReportItem.kt b/app/src/main/java/org/stepic/droid/model/DownloadReportItem.kt
new file mode 100644
index 0000000000..78371f34de
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/model/DownloadReportItem.kt
@@ -0,0 +1,9 @@
+package org.stepic.droid.model
+
+data class DownloadReportItem(
+ val mBytesDownloaded: Int,
+ val mBytesTotal: Int,
+ val mColumnStatus: Int,
+ val mDownloadId: Int,
+ val mColumnReason: Int
+)
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/preferences/SharedPreferenceHelper.java b/app/src/main/java/org/stepic/droid/preferences/SharedPreferenceHelper.java
index d4c1338d05..1ea5fea5bc 100644
--- a/app/src/main/java/org/stepic/droid/preferences/SharedPreferenceHelper.java
+++ b/app/src/main/java/org/stepic/droid/preferences/SharedPreferenceHelper.java
@@ -32,11 +32,13 @@ public SharedPreferenceHelper() {
mContext = MainApplication.getAppContext();
}
+
public enum PreferenceType {
LOGIN("login preference"),
WIFI("wifi_preference"),
VIDEO_QUALITY("video_quality_preference"),
- TEMP("temporary");
+ TEMP("temporary"),
+ VIDEO_SETTINGS("video_settings");
private String description;
@@ -49,6 +51,30 @@ private String getStoreName() {
}
}
+ public void storeVideoPlaybackRate(@NotNull VideoPlaybackRate videoPlaybackRate) {
+ int videoIndex = videoPlaybackRate.getIndex();
+ put(PreferenceType.VIDEO_SETTINGS, VIDEO_RATE_PREF_KEY, videoIndex);
+ }
+
+ @NotNull
+ public VideoPlaybackRate getVideoPlaybackRate() {
+ int index = getInt(PreferenceType.VIDEO_SETTINGS, VIDEO_RATE_PREF_KEY);
+
+ for (VideoPlaybackRate item : VideoPlaybackRate.values()) {
+ if (index == item.getIndex()) return item;
+ }
+
+ return VideoPlaybackRate.x1_0;//default
+ }
+
+ public boolean isOpenInExternal() {
+ return getBoolean(PreferenceType.VIDEO_SETTINGS, VIDEO_EXTERNAL_PREF_KEY);
+ }
+
+ public void setOpenInExternal(boolean isOpenInExternal) {
+ put(PreferenceType.VIDEO_SETTINGS, VIDEO_EXTERNAL_PREF_KEY, isOpenInExternal);
+ }
+
public void storeProfile(Profile profile) {
//todo save picture of user profile
//todo validate profile from the server with cached profile and make restore to cache. make
@@ -222,5 +248,7 @@ private boolean getBoolean(PreferenceType preferenceType, String key) {
private final String IS_SOCIAL = "is_social_key";
private final String VIDEO_QUALITY_KEY = "video_quality_key";
private final String TEMP_POSITION_KEY = "temp_position_key";
+ private final String VIDEO_RATE_PREF_KEY = "video_rate_pref_key";
+ private final String VIDEO_EXTERNAL_PREF_KEY = "video_external_pref_key";
}
diff --git a/app/src/main/java/org/stepic/droid/preferences/UserPreferences.java b/app/src/main/java/org/stepic/droid/preferences/UserPreferences.java
index 761e58af73..7299be4daf 100644
--- a/app/src/main/java/org/stepic/droid/preferences/UserPreferences.java
+++ b/app/src/main/java/org/stepic/droid/preferences/UserPreferences.java
@@ -100,4 +100,20 @@ public void storeQualityVideo(String videoQuality) {
mSharedPreferenceHelper.storeVideoQuality(videoQuality);
}
+ public VideoPlaybackRate getVideoPlaybackRate() {
+ return mSharedPreferenceHelper.getVideoPlaybackRate();
+ }
+
+ public void setVideoPlaybackRate(VideoPlaybackRate rate) {
+ mSharedPreferenceHelper.storeVideoPlaybackRate(rate);
+ }
+
+ public boolean isOpenInExternal() {
+ return mSharedPreferenceHelper.isOpenInExternal();
+ }
+
+ public void setOpenInExternal(boolean isOpenInExternal) {
+ mSharedPreferenceHelper.setOpenInExternal(isOpenInExternal);
+ }
+
}
diff --git a/app/src/main/java/org/stepic/droid/preferences/VideoPlaybackRate.kt b/app/src/main/java/org/stepic/droid/preferences/VideoPlaybackRate.kt
new file mode 100644
index 0000000000..3cd1faa27d
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/preferences/VideoPlaybackRate.kt
@@ -0,0 +1,20 @@
+package org.stepic.droid.preferences
+
+import android.graphics.drawable.Drawable
+import android.support.v4.content.ContextCompat
+import org.stepic.droid.R
+import org.stepic.droid.base.MainApplication
+import java.util.*
+
+enum class VideoPlaybackRate internal constructor(val index: Int, val rateFloat: Float, val icon: Drawable) {
+ x0_5(0, 0.5f, ContextCompat.getDrawable(MainApplication.getAppContext(), R.drawable.ic_playbackrate_0_5_light)),
+ x0_75(1, 0.75f, ContextCompat.getDrawable(MainApplication.getAppContext(), R.drawable.ic_playbackrate_0_75_light)),
+ x1_0(2, 1f, ContextCompat.getDrawable(MainApplication.getAppContext(), R.drawable.ic_playbackrate_1_light)),
+ x1_25(3, 1.25f, ContextCompat.getDrawable(MainApplication.getAppContext(), R.drawable.ic_playbackrate_1_25_light)),
+ x1_5(4, 1.5f, ContextCompat.getDrawable(MainApplication.getAppContext(), R.drawable.ic_playbackrate_1_5_light)),
+ x2(5, 2f, ContextCompat.getDrawable(MainApplication.getAppContext(), R.drawable.ic_playbackrate_2_0_light));
+
+ fun getAllOptions(): List {
+ return Arrays.asList(*VideoPlaybackRate.values())
+ }
+}
diff --git a/app/src/main/java/org/stepic/droid/receivers/DownloadCompleteReceiver.java b/app/src/main/java/org/stepic/droid/receivers/DownloadCompleteReceiver.java
index 1056331455..766354b163 100644
--- a/app/src/main/java/org/stepic/droid/receivers/DownloadCompleteReceiver.java
+++ b/app/src/main/java/org/stepic/droid/receivers/DownloadCompleteReceiver.java
@@ -5,11 +5,16 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
-import android.os.AsyncTask;
+import android.os.Handler;
+import android.util.Log;
+
+import com.squareup.otto.Bus;
import org.stepic.droid.base.MainApplication;
+import org.stepic.droid.events.video.VideoCachedOnDiskEvent;
import org.stepic.droid.model.CachedVideo;
import org.stepic.droid.model.DownloadEntity;
+import org.stepic.droid.model.Lesson;
import org.stepic.droid.model.Step;
import org.stepic.droid.preferences.UserPreferences;
import org.stepic.droid.store.ICancelSniffer;
@@ -18,6 +23,7 @@
import org.stepic.droid.util.RWLocks;
import java.io.File;
+import java.util.concurrent.ExecutorService;
import javax.inject.Inject;
@@ -29,10 +35,15 @@ public class DownloadCompleteReceiver extends BroadcastReceiver {
DatabaseFacade mDatabaseFacade;
@Inject
IStoreStateManager mStoreStateManager;
+ @Inject
+ Bus bus;
@Inject
ICancelSniffer mCancelSniffer;
+ @Inject
+ ExecutorService mThreadSingleThreadExecutor;
+
public DownloadCompleteReceiver() {
MainApplication.component().inject(this);
}
@@ -40,50 +51,64 @@ public DownloadCompleteReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final long referenceId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
- AsyncTask task = new AsyncTask() {
+
+ mThreadSingleThreadExecutor.execute(new Runnable() {
@Override
- protected Void doInBackground(Void[] params) {
- try {
- RWLocks.DownloadLock.writeLock().lock();
-
- DownloadEntity downloadEntity = mDatabaseFacade.getDownloadEntityIfExist(referenceId);
- if (downloadEntity != null) {
- long video_id = downloadEntity.getVideoId();
- long step_id = downloadEntity.getStepId();
- mDatabaseFacade.deleteDownloadEntityByDownloadId(referenceId);
-
-
- File downloadFolderAndFile = new File(mUserPrefs.getUserDownloadFolder(), video_id + "");
- String path = Uri.fromFile(downloadFolderAndFile).getPath();
-
- if (mCancelSniffer.isStepIdCanceled(step_id)) {
- File file = new File(path);
- if (file.exists()) {
- file.delete();
- }
- mCancelSniffer.removeStepIdCancel(step_id);
- }
- {
- //is not canceled
- CachedVideo cachedVideo = new CachedVideo(step_id, video_id, path, downloadEntity.getThumbnail());
- cachedVideo.setQuality(downloadEntity.getQuality());
- mDatabaseFacade.addVideo(cachedVideo);
-
- Step step = mDatabaseFacade.getStepById(step_id);
- step.set_cached(true);
- step.set_loading(false);
- mDatabaseFacade.updateOnlyCachedLoadingStep(step);
- mStoreStateManager.updateUnitLessonState(step.getLesson());
- }
+ public void run() {
+ blockForInBackground(referenceId);
+ Log.d("thread", Thread.currentThread().getName()+ " ");
+ }
+ });
+ }
+
+ private void blockForInBackground(final long referenceId) {
+ try {
+ RWLocks.DownloadLock.writeLock().lock();
+
+ DownloadEntity downloadEntity = mDatabaseFacade.getDownloadEntityIfExist(referenceId);
+ if (downloadEntity != null) {
+ long video_id = downloadEntity.getVideoId();
+ final long step_id = downloadEntity.getStepId();
+ mDatabaseFacade.deleteDownloadEntityByDownloadId(referenceId);
+
+
+ File downloadFolderAndFile = new File(mUserPrefs.getUserDownloadFolder(), video_id + "");
+ String path = Uri.fromFile(downloadFolderAndFile).getPath();
+
+ if (mCancelSniffer.isStepIdCanceled(step_id)) {
+ File file = new File(path);
+ if (file.exists()) {
+ file.delete();
}
- } finally {
- RWLocks.DownloadLock.writeLock().unlock();
+ mCancelSniffer.removeStepIdCancel(step_id);
+ }
+ {
+ //is not canceled
+ final CachedVideo cachedVideo = new CachedVideo(step_id, video_id, path, downloadEntity.getThumbnail());
+ cachedVideo.setQuality(downloadEntity.getQuality());
+ mDatabaseFacade.addVideo(cachedVideo);
+
+ final Step step = mDatabaseFacade.getStepById(step_id);
+ step.set_cached(true);
+ step.set_loading(false);
+ mDatabaseFacade.updateOnlyCachedLoadingStep(step);
+ mStoreStateManager.updateUnitLessonState(step.getLesson());
+ final Lesson lesson = mDatabaseFacade.getLessonById(step.getLesson());
+ Handler mainHandler = new Handler(MainApplication.getAppContext().getMainLooper());
+ //Say to ui that ui is cached now
+ Runnable myRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (lesson != null)
+ bus.post(new VideoCachedOnDiskEvent(step_id, lesson, cachedVideo));
+ }
+ };
+ mainHandler.post(myRunnable);
}
- return null;
- //end critical section
}
- };
- task.execute();
+ } finally {
+ RWLocks.DownloadLock.writeLock().unlock();
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/org/stepic/droid/services/LoadService.java b/app/src/main/java/org/stepic/droid/services/LoadService.java
index 54b15a63f2..a266775a7e 100644
--- a/app/src/main/java/org/stepic/droid/services/LoadService.java
+++ b/app/src/main/java/org/stepic/droid/services/LoadService.java
@@ -20,6 +20,7 @@
import org.stepic.droid.model.Step;
import org.stepic.droid.model.Unit;
import org.stepic.droid.model.Video;
+import org.stepic.droid.model.VideoUrl;
import org.stepic.droid.preferences.UserPreferences;
import org.stepic.droid.store.ICancelSniffer;
import org.stepic.droid.store.IStoreStateManager;
@@ -159,10 +160,24 @@ private void addDownload(String url, long fileId, String title, Step step) {
}
if (!mDb.isExistDownloadEntityByVideoId(fileId) && !downloadFolderAndFile.exists()) {
- long downloadId = mSystemDownloadManager.enqueue(request);
+
+ String videoQuality = null;
+ try {
+ for (VideoUrl urlItem : step.getBlock().getVideo().getUrls()) {
+ if (urlItem.getUrl().trim().equals(url)) {
+ videoQuality = urlItem.getQuality();
+ break;
+ }
+ }
+ }
+ catch (NullPointerException npe){
+ videoQuality = mUserPrefs.getQualityVideo();
+ }
+
+ long downloadId = mSystemDownloadManager.enqueue(request);
String local_thumbnail = fileId + AppConstants.THUMBNAIL_POSTFIX_EXTENSION;
String thumbnailsPath = FileUtil.saveImageToDisk(local_thumbnail, step.getBlock().getVideo().getThumbnail(), mUserPrefs.getUserDownloadFolder());
- final DownloadEntity newEntity = new DownloadEntity(downloadId, step.getId(), fileId, thumbnailsPath, mUserPrefs.getQualityVideo());
+ final DownloadEntity newEntity = new DownloadEntity(downloadId, step.getId(), fileId, thumbnailsPath, videoQuality);
mDb.addDownloadEntity(newEntity);
}
} catch (SecurityException ex) {
diff --git a/app/src/main/java/org/stepic/droid/services/PlaybackService.java b/app/src/main/java/org/stepic/droid/services/PlaybackService.java
new file mode 100644
index 0000000000..5e13c4aff5
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/services/PlaybackService.java
@@ -0,0 +1,2111 @@
+///*****************************************************************************
+// * PlaybackService.java
+// *****************************************************************************
+// * Copyright © 2011-2015 VLC authors and VideoLAN
+// *
+// * This program is free software; you can redistribute it and/or modify
+// * it under the terms of the GNU General Public License as published by
+// * the Free Software Foundation; either version 2 of the License, or
+// * (at your option) any later version.
+// *
+// * This program is distributed in the hope that it will be useful,
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// * GNU General Public License for more details.
+// *
+// * You should have received a copy of the GNU General Public License
+// * along with this program; if not, write to the Free Software
+// * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+// *****************************************************************************/
+//
+//package org.stepic.droid.services;
+//
+//import android.annotation.TargetApi;
+//import android.app.Notification;
+//import android.app.PendingIntent;
+//import android.app.Service;
+//import android.content.BroadcastReceiver;
+//import android.content.ComponentName;
+//import android.content.Context;
+//import android.content.Intent;
+//import android.content.IntentFilter;
+//import android.content.ServiceConnection;
+//import android.content.SharedPreferences;
+//import android.content.pm.PackageManager;
+//import android.graphics.Bitmap;
+//import android.graphics.BitmapFactory;
+//import android.media.AudioManager;
+//import android.media.AudioManager.OnAudioFocusChangeListener;
+//import android.net.Uri;
+//import android.os.Binder;
+//import android.os.Build;
+//import android.os.Bundle;
+//import android.os.Handler;
+//import android.os.IBinder;
+//import android.os.Message;
+//import android.os.PowerManager;
+//import android.preference.PreferenceManager;
+//import android.support.annotation.MainThread;
+//import android.support.annotation.Nullable;
+//import android.support.v4.app.NotificationManagerCompat;
+//import android.support.v4.content.LocalBroadcastManager;
+//import android.support.v4.media.MediaMetadataCompat;
+//import android.support.v4.media.session.MediaSessionCompat;
+//import android.support.v4.media.session.PlaybackStateCompat;
+//import android.support.v7.app.NotificationCompat;
+//import android.telephony.PhoneStateListener;
+//import android.telephony.TelephonyManager;
+//import android.text.TextUtils;
+//import android.util.Log;
+//import android.widget.Toast;
+//
+//import org.videolan.libvlc.IVLCVout;
+//import org.videolan.libvlc.LibVLC;
+//import org.videolan.libvlc.Media;
+//import org.videolan.libvlc.MediaList;
+//import org.videolan.libvlc.MediaPlayer;
+//import org.videolan.libvlc.util.AndroidUtil;
+//import org.videolan.vlc.gui.AudioPlayerContainerActivity;
+//import org.videolan.vlc.gui.helpers.AudioUtil;
+//import org.videolan.vlc.gui.preferences.PreferencesActivity;
+//import org.videolan.vlc.gui.preferences.PreferencesFragment;
+//import org.videolan.vlc.gui.video.VideoPlayerActivity;
+//import org.videolan.vlc.media.MediaDatabase;
+//import org.videolan.vlc.media.MediaUtils;
+//import org.videolan.vlc.media.MediaWrapper;
+//import org.videolan.vlc.media.MediaWrapperList;
+//import org.videolan.vlc.util.AndroidDevices;
+//import org.videolan.vlc.util.FileUtils;
+//import org.videolan.vlc.util.Strings;
+//import org.videolan.vlc.util.Util;
+//import org.videolan.vlc.util.VLCInstance;
+//import org.videolan.vlc.util.VLCOptions;
+//import org.videolan.vlc.util.WeakHandler;
+//import org.videolan.vlc.widget.VLCAppWidgetProvider;
+//
+//import java.io.File;
+//import java.net.URI;
+//import java.net.URISyntaxException;
+//import java.util.ArrayList;
+//import java.util.Calendar;
+//import java.util.Collections;
+//import java.util.List;
+//import java.util.Locale;
+//import java.util.Random;
+//import java.util.Stack;
+//import java.util.concurrent.atomic.AtomicBoolean;
+//
+//public class PlaybackService extends Service implements IVLCVout.Callback {
+//
+// private static final String TAG = "VLC/PlaybackService";
+//
+// private static final int SHOW_PROGRESS = 0;
+// private static final int SHOW_TOAST = 1;
+// public static final String ACTION_REMOTE_GENERIC = Strings.buildPkgString("remote.");
+// public static final String ACTION_REMOTE_BACKWARD = ACTION_REMOTE_GENERIC+"Backward";
+// public static final String ACTION_REMOTE_PLAY = ACTION_REMOTE_GENERIC+"Play";
+// public static final String ACTION_REMOTE_PLAYPAUSE = ACTION_REMOTE_GENERIC+"PlayPause";
+// public static final String ACTION_REMOTE_PAUSE = ACTION_REMOTE_GENERIC+"Pause";
+// public static final String ACTION_REMOTE_STOP = ACTION_REMOTE_GENERIC+"Stop";
+// public static final String ACTION_REMOTE_FORWARD = ACTION_REMOTE_GENERIC+"Forward";
+// public static final String ACTION_REMOTE_LAST_PLAYLIST = ACTION_REMOTE_GENERIC+"LastPlaylist";
+// public static final String ACTION_REMOTE_LAST_VIDEO_PLAYLIST = ACTION_REMOTE_GENERIC+"LastVideoPlaylist";
+// public static final String ACTION_REMOTE_SWITCH_VIDEO = ACTION_REMOTE_GENERIC+"SwitchToVideo";
+//
+// public interface Callback {
+// void update();
+// void updateProgress();
+// void onMediaEvent(Media.Event event);
+// void onMediaPlayerEvent(MediaPlayer.Event event);
+// }
+//
+// private class LocalBinder extends Binder {
+// PlaybackService getService() {
+// return PlaybackService.this;
+// }
+// }
+// public static PlaybackService getService(IBinder iBinder) {
+// LocalBinder binder = (LocalBinder) iBinder;
+// return binder.getService();
+// }
+//
+// private SharedPreferences mSettings;
+// private final IBinder mBinder = new LocalBinder();
+// private MediaWrapperList mMediaList = new MediaWrapperList();
+// private MediaPlayer mMediaPlayer;
+// private boolean mParsed = false;
+// private boolean mSeekable = false;
+// private boolean mPausable = false;
+// private boolean mIsAudioTrack = false;
+// private boolean mHasHdmiAudio = false;
+// private boolean mSwitchingToVideo = false;
+//
+// final private ArrayList mCallbacks = new ArrayList();
+// private boolean mDetectHeadset = true;
+// private boolean mPebbleEnabled;
+// private PowerManager.WakeLock mWakeLock;
+// private final AtomicBoolean mExpanding = new AtomicBoolean(false);
+//
+// private static boolean mWasPlayingAudio = false; // used only if readPhoneState returns true
+//
+// // Index management
+// /**
+// * Stack of previously played indexes, used in shuffle mode
+// */
+// private Stack mPrevious;
+// private int mCurrentIndex; // Set to -1 if no media is currently loaded
+// private int mPrevIndex; // Set to -1 if no previous media
+// private int mNextIndex; // Set to -1 if no next media
+//
+// // Playback management
+//
+// MediaSessionCompat mMediaSession;
+// protected MediaSessionCallback mSessionCallback;
+// private static final long PLAYBACK_ACTIONS = PlaybackStateCompat.ACTION_PAUSE
+// | PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_STOP
+// | PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS;
+//
+// public static final int TYPE_AUDIO = 0;
+// public static final int TYPE_VIDEO = 1;
+//
+// public static final int REPEAT_NONE = 0;
+// public static final int REPEAT_ONE = 1;
+// public static final int REPEAT_ALL = 2;
+// private boolean mShuffling = false;
+// private int mRepeating = REPEAT_NONE;
+// private Random mRandom = null; // Used in shuffling process
+// private long mSavedTime = 0l;
+// private boolean mHasAudioFocus = false;
+// // RemoteControlClient-related
+// /**
+// * RemoteControlClient is for lock screen playback control.
+// */
+// private RemoteControlClientReceiver mRemoteControlClientReceiver = null;
+// /**
+// * Last widget position update timestamp
+// */
+// private long mWidgetPositionTimestamp = Calendar.getInstance().getTimeInMillis();
+// private ComponentName mRemoteControlClientReceiverComponent;
+//
+// private static LibVLC LibVLC() {
+// return VLCInstance.get();
+// }
+//
+// private MediaPlayer newMediaPlayer() {
+// final MediaPlayer mp = new MediaPlayer(LibVLC());
+// final String aout = VLCOptions.getAout(mSettings);
+// if (mp.setAudioOutput(aout) && aout.equals("android_audiotrack")) {
+// mIsAudioTrack = true;
+// if (mHasHdmiAudio)
+// mp.setAudioOutputDevice("hdmi");
+// } else
+// mIsAudioTrack = false;
+// mp.getVLCVout().addCallback(this);
+//
+// return mp;
+// }
+//
+// private static boolean readPhoneState() {
+// return !AndroidUtil.isFroyoOrLater();
+// }
+//
+// @Override
+// public void onCreate() {
+// super.onCreate();
+//
+// mSettings = PreferenceManager.getDefaultSharedPreferences(this);
+// mMediaPlayer = newMediaPlayer();
+// mMediaPlayer.setEqualizer(VLCOptions.getEqualizer(this));
+//
+// if (!VLCInstance.testCompatibleCPU(this)) {
+// stopSelf();
+// return;
+// }
+//
+// if (!AndroidDevices.hasTsp() && !AndroidDevices.hasPlayServices())
+// AndroidDevices.setRemoteControlReceiverEnabled(true);
+//
+// mDetectHeadset = mSettings.getBoolean("enable_headset_detection", true);
+//
+// mCurrentIndex = -1;
+// mPrevIndex = -1;
+// mNextIndex = -1;
+// mPrevious = new Stack();
+// mRemoteControlClientReceiverComponent = new ComponentName(BuildConfig.APPLICATION_ID,
+// RemoteControlClientReceiver.class.getName());
+//
+// // Make sure the audio player will acquire a wake-lock while playing. If we don't do
+// // that, the CPU might go to sleep while the song is playing, causing playback to stop.
+// PowerManager pm = (PowerManager) VLCApplication.getAppContext().getSystemService(Context.POWER_SERVICE);
+// mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+//
+// IntentFilter filter = new IntentFilter();
+// filter.setPriority(Integer.MAX_VALUE);
+// filter.addAction(ACTION_REMOTE_BACKWARD);
+// filter.addAction(ACTION_REMOTE_PLAYPAUSE);
+// filter.addAction(ACTION_REMOTE_PLAY);
+// filter.addAction(ACTION_REMOTE_PAUSE);
+// filter.addAction(ACTION_REMOTE_STOP);
+// filter.addAction(ACTION_REMOTE_FORWARD);
+// filter.addAction(ACTION_REMOTE_LAST_PLAYLIST);
+// filter.addAction(ACTION_REMOTE_LAST_VIDEO_PLAYLIST);
+// filter.addAction(ACTION_REMOTE_SWITCH_VIDEO);
+// filter.addAction(VLCAppWidgetProvider.ACTION_WIDGET_INIT);
+// filter.addAction(Intent.ACTION_HEADSET_PLUG);
+// filter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+// filter.addAction(VLCApplication.SLEEP_INTENT);
+// registerReceiver(mReceiver, filter);
+// registerV21();
+//
+// boolean stealRemoteControl = mSettings.getBoolean("enable_steal_remote_control", false);
+//
+// if (!AndroidUtil.isFroyoOrLater() || stealRemoteControl) {
+// /* Backward compatibility for API 7 */
+// filter = new IntentFilter();
+// if (stealRemoteControl)
+// filter.setPriority(Integer.MAX_VALUE);
+// filter.addAction(Intent.ACTION_MEDIA_BUTTON);
+// mRemoteControlClientReceiver = new RemoteControlClientReceiver();
+// registerReceiver(mRemoteControlClientReceiver, filter);
+// }
+// try {
+// getPackageManager().getPackageInfo("com.getpebble.android", PackageManager.GET_ACTIVITIES);
+// mPebbleEnabled = true;
+// } catch (PackageManager.NameNotFoundException e) {
+// mPebbleEnabled = false;
+// }
+//
+// if (readPhoneState()) {
+// initPhoneListener();
+// TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
+// tm.listen(mPhoneStateListener, mPhoneEvents);
+// }
+// }
+//
+// @Override
+// public int onStartCommand(Intent intent, int flags, int startId) {
+// if (intent == null)
+// return START_STICKY;
+// if(ACTION_REMOTE_PLAYPAUSE.equals(intent.getAction())){
+// if (hasCurrentMedia())
+// return START_STICKY;
+// else
+// loadLastPlaylist(TYPE_AUDIO);
+// } else if (ACTION_REMOTE_PLAY.equals(intent.getAction())) {
+// if (hasCurrentMedia())
+// play();
+// else
+// loadLastPlaylist(TYPE_AUDIO);
+// }
+// updateWidget();
+// return super.onStartCommand(intent, flags, startId);
+// }
+//
+// @Override
+// public void onDestroy() {
+// super.onDestroy();
+// stop();
+//
+// if (!AndroidDevices.hasTsp() && !AndroidDevices.hasPlayServices())
+// AndroidDevices.setRemoteControlReceiverEnabled(false);
+//
+// if (mWakeLock.isHeld())
+// mWakeLock.release();
+// unregisterReceiver(mReceiver);
+// if (mReceiverV21 != null)
+// unregisterReceiver(mReceiverV21);
+// if (mRemoteControlClientReceiver != null) {
+// unregisterReceiver(mRemoteControlClientReceiver);
+// mRemoteControlClientReceiver = null;
+// }
+// mMediaPlayer.release();
+//
+// if (readPhoneState()) {
+// TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
+// tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+// }
+// }
+//
+// @Override
+// public IBinder onBind(Intent intent) {
+// return mBinder;
+// }
+//
+// @Override
+// public boolean onUnbind(Intent intent) {
+// if (!hasCurrentMedia())
+// stopSelf();
+// return true;
+// }
+//
+// public IVLCVout getVLCVout() {
+// return mMediaPlayer.getVLCVout();
+// }
+//
+// private final OnAudioFocusChangeListener mAudioFocusListener = AndroidUtil.isFroyoOrLater() ?
+// createOnAudioFocusChangeListener() : null;
+//
+// @TargetApi(Build.VERSION_CODES.FROYO)
+// private OnAudioFocusChangeListener createOnAudioFocusChangeListener() {
+// return new OnAudioFocusChangeListener() {
+// private boolean mLossTransient = false;
+// private boolean mLossTransientCanDuck = false;
+//
+// @Override
+// public void onAudioFocusChange(int focusChange) {
+// /*
+// * Pause playback during alerts and notifications
+// */
+// switch (focusChange) {
+// case AudioManager.AUDIOFOCUS_LOSS:
+// Log.i(TAG, "AUDIOFOCUS_LOSS");
+// // Pause playback
+// changeAudioFocus(false);
+// pause();
+// break;
+// case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
+// Log.i(TAG, "AUDIOFOCUS_LOSS_TRANSIENT");
+// // Pause playback
+// if (mMediaPlayer.isPlaying()) {
+// mLossTransient = true;
+// mMediaPlayer.pause();
+// }
+// break;
+// case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
+// Log.i(TAG, "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK");
+// // Lower the volume
+// if (mMediaPlayer.isPlaying()) {
+// mMediaPlayer.setVolume(36);
+// mLossTransientCanDuck = true;
+// }
+// break;
+// case AudioManager.AUDIOFOCUS_GAIN:
+// Log.i(TAG, "AUDIOFOCUS_GAIN: " + mLossTransientCanDuck + ", " + mLossTransient);
+// // Resume playback
+// if (mLossTransientCanDuck) {
+// mMediaPlayer.setVolume(100);
+// mLossTransientCanDuck = false;
+// }
+// if (mLossTransient) {
+// mMediaPlayer.play();
+// mLossTransient = false;
+// }
+// break;
+// }
+// }
+// };
+// }
+//
+// @TargetApi(Build.VERSION_CODES.FROYO)
+// private void changeAudioFocus(boolean acquire) {
+// final AudioManager am = (AudioManager)getSystemService(AUDIO_SERVICE);
+// if (am == null)
+// return;
+//
+// if (acquire) {
+// if (!mHasAudioFocus) {
+// final int result = am.requestAudioFocus(mAudioFocusListener,
+// AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
+// if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+// am.setParameters("bgm_state=true");
+// mHasAudioFocus = true;
+// }
+// }
+// } else {
+// if (mHasAudioFocus) {
+// final int result = am.abandonAudioFocus(mAudioFocusListener);
+// am.setParameters("bgm_state=false");
+// mHasAudioFocus = false;
+// }
+// }
+// }
+//
+//
+// @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+// private void registerV21() {
+// final IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG);
+// registerReceiver(mReceiverV21, intentFilter);
+// }
+//
+// private final BroadcastReceiver mReceiverV21 = AndroidUtil.isLolliPopOrLater() ? new BroadcastReceiver()
+// {
+// @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+// @Override
+// public void onReceive(Context context, Intent intent) {
+// final String action = intent.getAction();
+// if (action == null)
+// return;
+// if (action.equalsIgnoreCase(AudioManager.ACTION_HDMI_AUDIO_PLUG)) {
+// mHasHdmiAudio = intent.getIntExtra(AudioManager.EXTRA_AUDIO_PLUG_STATE, 0) == 1;
+// if (mMediaPlayer != null && mIsAudioTrack)
+// mMediaPlayer.setAudioOutputDevice(mHasHdmiAudio ? "hdmi" : "stereo");
+// }
+// }
+// } : null;
+//
+// private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+// @Override
+// public void onReceive(Context context, Intent intent) {
+// String action = intent.getAction();
+// int state = intent.getIntExtra("state", 0);
+// if( mMediaPlayer == null ) {
+// Log.w(TAG, "Intent received, but VLC is not loaded, skipping.");
+// return;
+// }
+//
+// // skip all headsets events if there is a call
+// TelephonyManager telManager = (TelephonyManager) VLCApplication.getAppContext().getSystemService(Context.TELEPHONY_SERVICE);
+// if (telManager != null && telManager.getCallState() != TelephonyManager.CALL_STATE_IDLE)
+// return;
+//
+// /*
+// * Launch the activity if needed
+// */
+// if (action.startsWith(ACTION_REMOTE_GENERIC) && !mMediaPlayer.isPlaying() && !hasCurrentMedia()) {
+// context.startActivity(getPackageManager().getLaunchIntentForPackage(getPackageName()));
+// }
+//
+// /*
+// * Remote / headset control events
+// */
+// if (action.equalsIgnoreCase(ACTION_REMOTE_PLAYPAUSE)) {
+// if (mMediaPlayer.isPlaying() && hasCurrentMedia())
+// pause();
+// else if (!mMediaPlayer.isPlaying() && hasCurrentMedia())
+// play();
+// } else if (action.equalsIgnoreCase(ACTION_REMOTE_PLAY)) {
+// if (!mMediaPlayer.isPlaying() && hasCurrentMedia())
+// play();
+// } else if (action.equalsIgnoreCase(ACTION_REMOTE_PAUSE)) {
+// if (mMediaPlayer.isPlaying() && hasCurrentMedia())
+// pause();
+// } else if (action.equalsIgnoreCase(ACTION_REMOTE_BACKWARD)) {
+// previous();
+// } else if (action.equalsIgnoreCase(ACTION_REMOTE_STOP)) {
+// stop();
+// } else if (action.equalsIgnoreCase(ACTION_REMOTE_FORWARD)) {
+// next();
+// } else if (action.equalsIgnoreCase(ACTION_REMOTE_LAST_PLAYLIST)) {
+// loadLastPlaylist(TYPE_AUDIO);
+// } else if (action.equalsIgnoreCase(ACTION_REMOTE_LAST_VIDEO_PLAYLIST)) {
+// loadLastPlaylist(TYPE_VIDEO);
+// } else if (action.equalsIgnoreCase(ACTION_REMOTE_SWITCH_VIDEO)) {
+// getCurrentMediaWrapper().removeFlags(MediaWrapper.MEDIA_FORCE_AUDIO);
+// switchToVideo();
+// } else if (action.equalsIgnoreCase(VLCAppWidgetProvider.ACTION_WIDGET_INIT)) {
+// updateWidget();
+// }
+//
+// /*
+// * headset plug events
+// */
+// if (mDetectHeadset && !mHasHdmiAudio) {
+// if (action.equalsIgnoreCase(AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {
+// Log.i(TAG, "Headset Removed.");
+// if (mMediaPlayer.isPlaying() && hasCurrentMedia())
+// pause();
+// }
+// else if (action.equalsIgnoreCase(Intent.ACTION_HEADSET_PLUG) && state != 0) {
+// Log.i(TAG, "Headset Inserted.");
+// if (!mMediaPlayer.isPlaying() && hasCurrentMedia())
+// play();
+// }
+// }
+//
+// /*
+// * Sleep
+// */
+// if (action.equalsIgnoreCase(VLCApplication.SLEEP_INTENT)) {
+// stop();
+// }
+// }
+// };
+//
+//
+// @Override
+// public void onNewLayout(IVLCVout vlcVout, int width, int height, int visibleWidth, int visibleHeight, int sarNum, int sarDen) {
+// }
+//
+// @Override
+// public void onSurfacesCreated(IVLCVout vlcVout) {
+// hideNotification(false);
+// }
+//
+// @Override
+// public void onSurfacesDestroyed(IVLCVout vlcVout) {
+// mSwitchingToVideo = false;
+// }
+//
+// @Override
+// public void onHardwareAccelerationError(IVLCVout vlcVout) {
+// }
+//
+// private final Media.EventListener mMediaListener = new Media.EventListener() {
+// @Override
+// public void onEvent(Media.Event event) {
+// switch (event.type) {
+// case Media.Event.MetaChanged:
+// /* Update Meta if file is already parsed */
+// if (mParsed && updateCurrentMeta(event.getMetaId()))
+// executeUpdate();
+// Log.i(TAG, "Media.Event.MetaChanged: " + event.getMetaId());
+// break;
+// case Media.Event.ParsedChanged:
+// Log.i(TAG, "Media.Event.ParsedChanged");
+// updateCurrentMeta(-1);
+// mParsed = true;
+// break;
+//
+// }
+// for (Callback callback : mCallbacks)
+// callback.onMediaEvent(event);
+// }
+// };
+//
+// /**
+// * Update current media meta and return true if player needs to be updated
+// *
+// * @param id of the Meta event received, -1 for none
+// * @return true if UI needs to be updated
+// */
+// private boolean updateCurrentMeta(int id) {
+// if (id == Media.Meta.Publisher)
+// return false;
+// final MediaWrapper mw = getCurrentMedia();
+// if (mw != null)
+// mw.updateMeta(mMediaPlayer);
+// return id != Media.Meta.NowPlaying || getCurrentMedia().getNowPlaying() != null;
+// }
+//
+// private final MediaPlayer.EventListener mMediaPlayerListener = new MediaPlayer.EventListener() {
+//
+// @Override
+// public void onEvent(MediaPlayer.Event event) {
+// switch (event.type) {
+// case MediaPlayer.Event.Playing:
+//
+// Log.i(TAG, "MediaPlayer.Event.Playing");
+// executeUpdate();
+// publishState(event.type);
+// executeUpdateProgress();
+//
+// final MediaWrapper mw = mMediaList.getMedia(mCurrentIndex);
+// if (mw != null) {
+// long length = mMediaPlayer.getLength();
+// MediaDatabase dbManager = MediaDatabase.getInstance();
+// MediaWrapper m = dbManager.getMedia(mw.getUri());
+// /**
+// * 1) There is a media to update
+// * 2) It has a length of 0
+// * (dynamic track loading - most notably the OGG container)
+// * 3) We were able to get a length even after parsing
+// * (don't want to replace a 0 with a 0)
+// */
+// if (m != null && m.getLength() == 0 && length > 0) {
+// dbManager.updateMedia(mw.getUri(),
+// MediaDatabase.INDEX_MEDIA_LENGTH, length);
+// }
+// }
+//
+// changeAudioFocus(true);
+// if (!mWakeLock.isHeld())
+// mWakeLock.acquire();
+// if (switchToVideo())
+// hideNotification();
+// else
+// showNotification();
+// break;
+// case MediaPlayer.Event.Paused:
+// Log.i(TAG, "MediaPlayer.Event.Paused");
+// executeUpdate();
+// publishState(event.type);
+// executeUpdateProgress();
+// if (mWakeLock.isHeld())
+// mWakeLock.release();
+// break;
+// case MediaPlayer.Event.Stopped:
+// Log.i(TAG, "MediaPlayer.Event.Stopped");
+// executeUpdate();
+// publishState(event.type);
+// executeUpdateProgress();
+// if (mWakeLock.isHeld())
+// mWakeLock.release();
+// changeAudioFocus(false);
+// break;
+// case MediaPlayer.Event.EndReached:
+// Log.i(TAG, "MediaPlayer.Event.EndReached");
+// executeUpdateProgress();
+// determinePrevAndNextIndices(true);
+// next();
+// if (mWakeLock.isHeld())
+// mWakeLock.release();
+// changeAudioFocus(false);
+// break;
+// case MediaPlayer.Event.EncounteredError:
+// showToast(getString(
+// R.string.invalid_location,
+// mMediaList.getMRL(mCurrentIndex)), Toast.LENGTH_SHORT);
+// executeUpdate();
+// executeUpdateProgress();
+// next();
+// if (mWakeLock.isHeld())
+// mWakeLock.release();
+// break;
+// case MediaPlayer.Event.TimeChanged:
+// break;
+// case MediaPlayer.Event.PositionChanged:
+// updateWidgetPosition(event.getPositionChanged());
+// break;
+// case MediaPlayer.Event.Vout:
+// break;
+// case MediaPlayer.Event.ESAdded:
+// if (event.getEsChangedType() == Media.Track.Type.Video && !switchToVideo()) {
+// /* Update notification content intent: resume video or resume audio activity */
+// updateMetadata();
+// }
+// break;
+// case MediaPlayer.Event.ESDeleted:
+// break;
+// case MediaPlayer.Event.PausableChanged:
+// mPausable = event.getPausable();
+// break;
+// case MediaPlayer.Event.SeekableChanged:
+// mSeekable = event.getSeekable();
+// break;
+// }
+// for (Callback callback : mCallbacks)
+// callback.onMediaPlayerEvent(event);
+// }
+// };
+//
+// private final MediaWrapperList.EventListener mListEventListener = new MediaWrapperList.EventListener() {
+//
+// @Override
+// public void onItemAdded(int index, String mrl) {
+// Log.i(TAG, "CustomMediaListItemAdded");
+// if(mCurrentIndex >= index && !mExpanding.get())
+// mCurrentIndex++;
+//
+// determinePrevAndNextIndices();
+// executeUpdate();
+// }
+//
+// @Override
+// public void onItemRemoved(int index, String mrl) {
+// Log.i(TAG, "CustomMediaListItemDeleted");
+// if (mCurrentIndex == index && !mExpanding.get()) {
+// // The current item has been deleted
+// mCurrentIndex--;
+// determinePrevAndNextIndices();
+// if (mNextIndex != -1)
+// next();
+// else if (mCurrentIndex != -1) {
+// playIndex(mCurrentIndex, 0);
+// } else
+// stop();
+// }
+//
+// if(mCurrentIndex > index && !mExpanding.get())
+// mCurrentIndex--;
+// determinePrevAndNextIndices();
+// executeUpdate();
+// }
+//
+// @Override
+// public void onItemMoved(int indexBefore, int indexAfter, String mrl) {
+// Log.i(TAG, "CustomMediaListItemMoved");
+// if (mCurrentIndex == indexBefore) {
+// mCurrentIndex = indexAfter;
+// if (indexAfter > indexBefore)
+// mCurrentIndex--;
+// } else if (indexBefore > mCurrentIndex
+// && indexAfter <= mCurrentIndex)
+// mCurrentIndex++;
+// else if (indexBefore < mCurrentIndex
+// && indexAfter > mCurrentIndex)
+// mCurrentIndex--;
+//
+// // If we are in random mode, we completely reset the stored previous track
+// // as their indices changed.
+// mPrevious.clear();
+//
+// determinePrevAndNextIndices();
+// executeUpdate();
+// }
+// };
+//
+// public boolean canSwitchToVideo() {
+// return hasCurrentMedia() && mMediaPlayer.getVideoTracksCount() > 0;
+// }
+//
+// @MainThread
+// public boolean switchToVideo() {
+// if (mMediaList.getMedia(mCurrentIndex).hasFlag(MediaWrapper.MEDIA_FORCE_AUDIO) || !canSwitchToVideo())
+// return false;
+// if (isVideoPlaying()) {//Player is already running, just send it an intent
+// mMediaPlayer.setVideoTrackEnabled(true);
+// LocalBroadcastManager.getInstance(this).sendBroadcast(
+// VideoPlayerActivity.getIntent(VideoPlayerActivity.PLAY_FROM_SERVICE,
+// getCurrentMediaWrapper(), false, mCurrentIndex));
+// } else if (!mSwitchingToVideo) {//Start the video player
+// Log.e(TAG, "startOpened", new Exception());
+// VideoPlayerActivity.startOpened(VLCApplication.getAppContext(),
+// getCurrentMediaWrapper().getUri(), mCurrentIndex);
+// mSwitchingToVideo = true;
+// }
+// return true;
+// }
+//
+// private void executeUpdate() {
+// executeUpdate(true);
+// }
+//
+// private void executeUpdate(Boolean updateWidget) {
+// for (Callback callback : mCallbacks) {
+// callback.update();
+// }
+// if (updateWidget)
+// updateWidget();
+// updateMetadata();
+// }
+//
+// private void executeUpdateProgress() {
+// for (Callback callback : mCallbacks) {
+// callback.updateProgress();
+// }
+// }
+//
+// /**
+// * Return the current media.
+// *
+// * @return The current media or null if there is not any.
+// */
+// @Nullable
+// private MediaWrapper getCurrentMedia() {
+// return mMediaList.getMedia(mCurrentIndex);
+// }
+//
+// /**
+// * Alias for mCurrentIndex >= 0
+// *
+// * @return True if a media is currently loaded, false otherwise
+// */
+// private boolean hasCurrentMedia() {
+// return mCurrentIndex >= 0 && mCurrentIndex < mMediaList.size();
+// }
+//
+// private final Handler mHandler = new AudioServiceHandler(this);
+//
+// private static class AudioServiceHandler extends WeakHandler {
+// public AudioServiceHandler(PlaybackService fragment) {
+// super(fragment);
+// }
+//
+// @Override
+// public void handleMessage(Message msg) {
+// PlaybackService service = getOwner();
+// if(service == null) return;
+//
+// switch (msg.what) {
+// case SHOW_PROGRESS:
+// if (service.mCallbacks.size() > 0) {
+// removeMessages(SHOW_PROGRESS);
+// service.executeUpdateProgress();
+// sendEmptyMessageDelayed(SHOW_PROGRESS, 1000);
+// }
+// break;
+// case SHOW_TOAST:
+// final Bundle bundle = msg.getData();
+// final String text = bundle.getString("text");
+// final int duration = bundle.getInt("duration");
+// Toast.makeText(VLCApplication.getAppContext(), text, duration).show();
+// break;
+// }
+// }
+// }
+//
+// @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+// private void showNotification() {
+// if (mMediaPlayer.getVLCVout().areViewsAttached())
+// return;
+// try {
+// boolean coverOnLockscreen = mSettings.getBoolean("lockscreen_cover", true);
+// MediaMetadataCompat metaData = mMediaSession.getController().getMetadata();
+// String title = metaData.getString(MediaMetadataCompat.METADATA_KEY_TITLE);
+// String artist = metaData.getString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST);
+// String album = metaData.getString(MediaMetadataCompat.METADATA_KEY_ALBUM);
+// Bitmap cover = coverOnLockscreen ?
+// metaData.getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART) :
+// AudioUtil.getCover(this, getCurrentMedia(), 512);
+// if (cover == null)
+// cover = BitmapFactory.decodeResource(VLCApplication.getAppContext().getResources(), R.drawable.icon);
+// Notification notification;
+//
+// //Watch notification dismissed
+// PendingIntent piStop = PendingIntent.getBroadcast(this, 0,
+// new Intent(ACTION_REMOTE_STOP), PendingIntent.FLAG_UPDATE_CURRENT);
+//
+// // add notification to status bar
+// NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
+// builder.setSmallIcon(R.drawable.ic_stat_vlc)
+// .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
+// .setContentTitle(title)
+// .setContentText(artist + " - " + album)
+// .setLargeIcon(cover)
+// .setTicker(title + " - " + artist)
+// .setAutoCancel(!mMediaPlayer.isPlaying())
+// .setOngoing(mMediaPlayer.isPlaying())
+// .setDeleteIntent(piStop);
+//
+//
+// PendingIntent pendingIntent;
+// if (canSwitchToVideo() && !mMediaList.getMedia(mCurrentIndex).hasFlag(MediaWrapper.MEDIA_FORCE_AUDIO) ) {
+// /* Resume VideoPlayerActivity from ACTION_REMOTE_SWITCH_VIDEO intent */
+// final Intent notificationIntent = new Intent(ACTION_REMOTE_SWITCH_VIDEO);
+// pendingIntent = PendingIntent.getBroadcast(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+// } else {
+// /* Resume AudioPlayerActivity */
+//
+// final Intent notificationIntent = getPackageManager().getLaunchIntentForPackage(getPackageName());
+// notificationIntent.setAction(AudioPlayerContainerActivity.ACTION_SHOW_PLAYER);
+// notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+// pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+// }
+//
+// builder.setContentIntent(pendingIntent);
+//
+// PendingIntent piBackward = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_REMOTE_BACKWARD), PendingIntent.FLAG_UPDATE_CURRENT);
+// PendingIntent piPlay = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_REMOTE_PLAYPAUSE), PendingIntent.FLAG_UPDATE_CURRENT);
+// PendingIntent piForward = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_REMOTE_FORWARD), PendingIntent.FLAG_UPDATE_CURRENT);
+//
+// builder.addAction(R.drawable.ic_previous_w, getString(R.string.previous), piBackward);
+// if (mMediaPlayer.isPlaying())
+// builder.addAction(R.drawable.ic_pause_w, getString(R.string.pause), piPlay);
+// else
+// builder.addAction(R.drawable.ic_play_w, getString(R.string.play), piPlay);
+// builder.addAction(R.drawable.ic_next_w, getString(R.string.next), piForward);
+//
+// builder.setStyle(new NotificationCompat.MediaStyle()
+// .setMediaSession(mMediaSession.getSessionToken())
+// .setShowActionsInCompactView(new int[] {0,1,2})
+// .setShowCancelButton(true)
+// .setCancelButtonIntent(piStop)
+// );
+//
+// notification = builder.build();
+//
+// startService(new Intent(this, PlaybackService.class));
+// if (!AndroidUtil.isLolliPopOrLater() || mMediaPlayer.isPlaying())
+// startForeground(3, notification);
+// else {
+// stopForeground(false);
+// NotificationManagerCompat.from(this).notify(3, notification);
+// }
+// }
+// catch (NoSuchMethodError e){
+// // Compat library is wrong on 3.2
+// // http://code.google.com/p/android/issues/detail?id=36359
+// // http://code.google.com/p/android/issues/detail?id=36502
+// }
+// }
+//
+// private void hideNotification() {
+// hideNotification(true);
+// }
+//
+// /**
+// * Hides the VLC notification and stops the service.
+// *
+// * @param stopPlayback True to also stop playback at the same time. Set to false to preserve playback (e.g. for vout events)
+// */
+// private void hideNotification(boolean stopPlayback) {
+// stopForeground(true);
+// if(stopPlayback)
+// stopSelf();
+// }
+//
+// @MainThread
+// public void pause() {
+// if (mPausable) {
+// savePosition();
+// mHandler.removeMessages(SHOW_PROGRESS);
+// // hideNotification(); <-- see event handler
+// mMediaPlayer.pause();
+// broadcastMetadata();
+// }
+// }
+//
+// @MainThread
+// public void play() {
+// if(hasCurrentMedia()) {
+// mMediaPlayer.play();
+// mHandler.sendEmptyMessage(SHOW_PROGRESS);
+// updateMetadata();
+// updateWidget();
+// broadcastMetadata();
+// }
+// }
+//
+// @MainThread
+// public void stop() {
+// if (mMediaSession != null) {
+// mMediaSession.setActive(false);
+// mMediaSession.release();
+// mMediaSession = null;
+// }
+// if (mMediaPlayer == null)
+// return;
+// savePosition();
+// final Media media = mMediaPlayer.getMedia();
+// if (media != null) {
+// media.setEventListener(null);
+// mMediaPlayer.setEventListener(null);
+// mMediaPlayer.stop();
+// mMediaPlayer.setMedia(null);
+// media.release();
+// }
+// mMediaList.removeEventListener(mListEventListener);
+// mCurrentIndex = -1;
+// mPrevious.clear();
+// mHandler.removeMessages(SHOW_PROGRESS);
+// hideNotification();
+// broadcastMetadata();
+// executeUpdate();
+// executeUpdateProgress();
+// changeAudioFocus(false);
+//
+// stopSelf();
+// }
+//
+// private void determinePrevAndNextIndices() {
+// determinePrevAndNextIndices(false);
+// }
+//
+// private void determinePrevAndNextIndices(boolean expand) {
+// if (expand) {
+// mExpanding.set(true);
+// mNextIndex = expand();
+// mExpanding.set(false);
+// } else {
+// mNextIndex = -1;
+// }
+// mPrevIndex = -1;
+//
+// if (mNextIndex == -1) {
+// // No subitems; play the next item.
+// int size = mMediaList.size();
+// mShuffling &= size > 2;
+//
+// // Repeating once doesn't change the index
+// if (mRepeating == REPEAT_ONE) {
+// mPrevIndex = mNextIndex = mCurrentIndex;
+// } else {
+//
+// if(mShuffling) {
+// if(mPrevious.size() > 0)
+// mPrevIndex = mPrevious.peek();
+// // If we've played all songs already in shuffle, then either
+// // reshuffle or stop (depending on RepeatType).
+// if(mPrevious.size() + 1 == size) {
+// if(mRepeating == REPEAT_NONE) {
+// mNextIndex = -1;
+// return;
+// } else {
+// mPrevious.clear();
+// }
+// }
+// if(mRandom == null) mRandom = new Random();
+// // Find a new index not in mPrevious.
+// do
+// {
+// mNextIndex = mRandom.nextInt(size);
+// }
+// while(mNextIndex == mCurrentIndex || mPrevious.contains(mNextIndex));
+//
+// } else {
+// // normal playback
+// if(mCurrentIndex > 0)
+// mPrevIndex = mCurrentIndex - 1;
+// if(mCurrentIndex + 1 < size)
+// mNextIndex = mCurrentIndex + 1;
+// else {
+// if(mRepeating == REPEAT_NONE) {
+// mNextIndex = -1;
+// } else {
+// mNextIndex = 0;
+// }
+// }
+// }
+// }
+// }
+// }
+//
+// private void initMediaSession() {
+// ComponentName mediaButtonEventReceiver = new ComponentName(this,
+// RemoteControlClientReceiver.class);
+// mSessionCallback = new MediaSessionCallback();
+// mMediaSession = new MediaSessionCompat(this, "VLC", mediaButtonEventReceiver, null);
+// mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
+// | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
+// mMediaSession.setCallback(mSessionCallback);
+// try {
+// mMediaSession.setActive(true);
+// } catch (NullPointerException e) {
+// // Some versions of KitKat do not support AudioManager.registerMediaButtonIntent
+// // with a PendingIntent. They will throw a NullPointerException, in which case
+// // they should be able to activate a MediaSessionCompat with only transport
+// // controls.
+// mMediaSession.setActive(false);
+// mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
+// mMediaSession.setActive(true);
+// }
+// }
+//
+// private final class MediaSessionCallback extends MediaSessionCompat.Callback {
+// @Override
+// public void onPlay() {
+// play();
+// }
+// @Override
+// public void onPause() {
+// pause();
+// }
+//
+// @Override
+// public void onStop() {
+// stop();
+// }
+//
+// @Override
+// public void onSkipToNext() {
+// next();
+// }
+//
+// @Override
+// public void onSkipToPrevious() {
+// previous();
+// }
+//
+// @Override
+// public void onSeekTo(long pos) {
+// setTime(pos);
+// }
+//
+// @Override
+// public void onFastForward() {
+// next();
+// }
+//
+// @Override
+// public void onRewind() {
+// previous();
+// }
+// }
+//
+// protected void updateMetadata() {
+// MediaWrapper media = getCurrentMedia();
+// if (media == null)
+// return;
+// if (mMediaSession == null)
+// initMediaSession();
+// String title = media.getNowPlaying();
+// if (title == null)
+// title = media.getTitle();
+// boolean coverOnLockscreen = mSettings.getBoolean("lockscreen_cover", true);
+// MediaMetadataCompat.Builder bob = new MediaMetadataCompat.Builder();
+// bob.putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
+// .putString(MediaMetadataCompat.METADATA_KEY_GENRE, MediaUtils.getMediaGenre(this, media))
+// .putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, media.getTrackNumber())
+// .putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, MediaUtils.getMediaReferenceArtist(this, media))
+// .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, MediaUtils.getMediaAlbum(this, media))
+// .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, media.getLength());
+// if (coverOnLockscreen) {
+// Bitmap cover = AudioUtil.getCover(this, media, 512);
+// if (cover != null && cover.getConfig() != null) //In case of format not supported
+// bob.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, cover.copy(cover.getConfig(), false));
+// }
+// mMediaSession.setMetadata(bob.build());
+//
+// //Send metadata to Pebble watch
+// if (mPebbleEnabled) {
+// final Intent i = new Intent("com.getpebble.action.NOW_PLAYING");
+// i.putExtra("artist", MediaUtils.getMediaArtist(this, media));
+// i.putExtra("album", MediaUtils.getMediaAlbum(this, media));
+// i.putExtra("track", media.getTitle());
+// sendBroadcast(i);
+// }
+// showNotification();
+// }
+//
+// protected void publishState(int state) {
+// if (mMediaSession == null)
+// return;
+// PlaybackStateCompat.Builder bob = new PlaybackStateCompat.Builder();
+// bob.setActions(PLAYBACK_ACTIONS);
+// switch (state) {
+// case MediaPlayer.Event.Playing:
+// bob.setState(PlaybackStateCompat.STATE_PLAYING, -1, 1);
+// break;
+// case MediaPlayer.Event.Stopped:
+// bob.setState(PlaybackStateCompat.STATE_STOPPED, -1, 0);
+// break;
+// default:
+// bob.setState(PlaybackStateCompat.STATE_PAUSED, -1, 0);
+// }
+// PlaybackStateCompat pbState = bob.build();
+// mMediaSession.setPlaybackState(pbState);
+// mMediaSession.setActive(state != PlaybackStateCompat.STATE_STOPPED);
+// }
+//
+// private void notifyTrackChanged() {
+// mHandler.sendEmptyMessage(SHOW_PROGRESS);
+// updateMetadata();
+// updateWidget();
+// broadcastMetadata();
+// }
+//
+// private void onMediaChanged() {
+// notifyTrackChanged();
+//
+// saveCurrentMedia();
+// determinePrevAndNextIndices();
+// }
+//
+// private void onMediaListChanged() {
+// saveMediaList();
+// determinePrevAndNextIndices();
+// executeUpdate();
+// }
+//
+// @MainThread
+// public void next() {
+// int size = mMediaList.size();
+//
+// mPrevious.push(mCurrentIndex);
+// mCurrentIndex = mNextIndex;
+// Log.d(TAG, "setting current to " + mCurrentIndex);
+// if (size == 0 || mCurrentIndex < 0 || mCurrentIndex >= size) {
+// if (mCurrentIndex < 0)
+// saveCurrentMedia();
+// Log.w(TAG, "Warning: invalid next index, aborted !");
+// //Close video player if started
+// LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(VideoPlayerActivity.EXIT_PLAYER));
+// stop();
+// return;
+// }
+// playIndex(mCurrentIndex, 0);
+// saveCurrentMedia();
+// }
+//
+// @MainThread
+// public void previous() {
+// int size = mMediaList.size();
+// if (hasPrevious() && mCurrentIndex > 0 && mMediaPlayer.getTime() < 2000l) {
+// mCurrentIndex = mPrevIndex;
+// if (mPrevious.size() > 0)
+// mPrevious.pop();
+// if (size == 0 || mPrevIndex < 0 || mCurrentIndex >= size) {
+// Log.w(TAG, "Warning: invalid previous index, aborted !");
+// stop();
+// return;
+// }
+// } else
+// setPosition(0f);
+//
+// playIndex(mCurrentIndex, 0);
+// saveCurrentMedia();
+// }
+//
+// @MainThread
+// public void shuffle() {
+// if (mShuffling)
+// mPrevious.clear();
+// mShuffling = !mShuffling;
+// savePosition();
+// determinePrevAndNextIndices();
+// }
+//
+// @MainThread
+// public void setRepeatType(int repeatType) {
+// mRepeating = repeatType;
+// savePosition();
+// determinePrevAndNextIndices();
+// }
+//
+// private void updateWidget() {
+// updateWidgetState();
+// updateWidgetCover();
+// }
+//
+// private void updateWidgetState() {
+// Intent i = new Intent(VLCAppWidgetProvider.ACTION_WIDGET_UPDATE);
+//
+// if (hasCurrentMedia()) {
+// final MediaWrapper media = getCurrentMedia();
+// i.putExtra("title", media.getTitle());
+// i.putExtra("artist", media.isArtistUnknown() && media.getNowPlaying() != null ?
+// media.getNowPlaying()
+// : MediaUtils.getMediaArtist(this, media));
+// }
+// else {
+// i.putExtra("title", getString(R.string.widget_default_text));
+// i.putExtra("artist", "");
+// }
+// i.putExtra("isplaying", mMediaPlayer.isPlaying());
+//
+// sendBroadcast(i);
+// }
+//
+// private void updateWidgetCover() {
+// Intent i = new Intent(VLCAppWidgetProvider.ACTION_WIDGET_UPDATE_COVER);
+//
+// Bitmap cover = hasCurrentMedia() ? AudioUtil.getCover(this, getCurrentMedia(), 64) : null;
+// i.putExtra("cover", cover);
+//
+// sendBroadcast(i);
+// }
+//
+// private void updateWidgetPosition(float pos) {
+// // no more than one widget update for each 1/50 of the song
+// long timestamp = Calendar.getInstance().getTimeInMillis();
+// if (!hasCurrentMedia()
+// || timestamp - mWidgetPositionTimestamp < getCurrentMedia().getLength() / 50)
+// return;
+//
+// updateWidgetState();
+//
+// mWidgetPositionTimestamp = timestamp;
+// Intent i = new Intent(VLCAppWidgetProvider.ACTION_WIDGET_UPDATE_POSITION);
+// i.putExtra("position", pos);
+// sendBroadcast(i);
+// }
+//
+// private void broadcastMetadata() {
+// MediaWrapper media = getCurrentMedia();
+// if (media == null || media.getType() != MediaWrapper.TYPE_AUDIO)
+// return;
+//
+// boolean playing = mMediaPlayer.isPlaying();
+//
+// Intent broadcast = new Intent("com.android.music.metachanged");
+// broadcast.putExtra("track", media.getTitle());
+// broadcast.putExtra("artist", media.getArtist());
+// broadcast.putExtra("album", media.getAlbum());
+// broadcast.putExtra("duration", media.getLength());
+// broadcast.putExtra("playing", playing);
+//
+// sendBroadcast(broadcast);
+// }
+//
+// public synchronized void loadLastPlaylist(int type) {
+// boolean audio = type == TYPE_AUDIO;
+// String currentMedia = mSettings.getString(audio ? "current_song" : "current_media", "");
+// if (currentMedia.equals(""))
+// return;
+// String[] locations = mSettings.getString(audio ? "audio_list" : "media_list", "").split(" ");
+// if (locations.length == 0)
+// return;
+//
+// List mediaPathList = new ArrayList(locations.length);
+// for (int i = 0 ; i < locations.length ; ++i)
+// mediaPathList.add(Uri.decode(locations[i]));
+//
+// mShuffling = mSettings.getBoolean(audio ? "audio_shuffling" : "media_shuffling", false);
+// mRepeating = mSettings.getInt(audio ? "audio_repeating" : "media_repeating", REPEAT_NONE);
+// int position = mSettings.getInt(audio ? "position_in_audio_list" : "position_in_media_list",
+// Math.max(0, mediaPathList.indexOf(currentMedia)));
+// long time = mSettings.getLong(audio ? "position_in_song" : "position_in_media", -1);
+// mSavedTime = time;
+// // load playlist
+// loadLocations(mediaPathList, position);
+// if (time > 0)
+// setTime(time);
+// if(!audio) {
+// boolean paused = mSettings.getBoolean(PreferencesActivity.VIDEO_PAUSED, !isPlaying());
+// float rate = mSettings.getFloat(PreferencesActivity.VIDEO_SPEED, getRate());
+// if (paused)
+// pause();
+// if (rate != 1.0f)
+// setRate(rate);
+// }
+// SharedPreferences.Editor editor = mSettings.edit();
+// editor.putInt(audio ? "position_in_audio_list" : "position_in_media_list", 0);
+// editor.putLong(audio ? "position_in_song" : "position_in_media", 0);
+// Util.commitPreferences(editor);
+// }
+//
+// private synchronized void saveCurrentMedia() {
+// boolean audio = true;
+// for (int i = 0; i < mMediaList.size(); i++) {
+// if (mMediaList.getMedia(i).getType() == MediaWrapper.TYPE_VIDEO)
+// audio = false;
+// }
+// SharedPreferences.Editor editor = mSettings.edit();
+// editor.putString(audio ? "current_song" : "current_media", mMediaList.getMRL(Math.max(mCurrentIndex, 0)));
+// Util.commitPreferences(editor);
+// }
+//
+// private synchronized void saveMediaList() {
+// if (getCurrentMedia() == null)
+// return;
+// StringBuilder locations = new StringBuilder();
+// boolean audio = true;
+// for (int i = 0; i < mMediaList.size(); i++) {
+// if (mMediaList.getMedia(i).getType() == MediaWrapper.TYPE_VIDEO)
+// audio = false;
+// locations.append(" ").append(Uri.encode(mMediaList.getMRL(i)));
+// }
+// //We save a concatenated String because putStringSet is APIv11.
+// SharedPreferences.Editor editor = mSettings.edit();
+// editor.putString(audio ? "audio_list" : "media_list", locations.toString().trim());
+// Util.commitPreferences(editor);
+// }
+//
+// private synchronized void savePosition(){
+// if (getCurrentMedia() == null)
+// return;
+// SharedPreferences.Editor editor = mSettings.edit();
+// boolean audio = true;
+// for (int i = 0; i < mMediaList.size(); i++) {
+// if (mMediaList.getMedia(i).getType() == MediaWrapper.TYPE_VIDEO)
+// audio = false;
+// }
+// editor.putBoolean(audio ? "audio_shuffling" : "media_shuffling", mShuffling);
+// editor.putInt(audio ? "audio_repeating" : "media_repeating", mRepeating);
+// editor.putInt(audio ? "position_in_audio_list" : "position_in_media_list", mCurrentIndex);
+// editor.putLong(audio ? "position_in_song" : "position_in_media", mMediaPlayer.getTime());
+// if(!audio) {
+// editor.putBoolean(PreferencesActivity.VIDEO_PAUSED, !isPlaying());
+// editor.putFloat(PreferencesActivity.VIDEO_SPEED, getRate());
+// }
+// Util.commitPreferences(editor);
+// }
+//
+// private boolean validateLocation(String location)
+// {
+// /* Check if the MRL contains a scheme */
+// if (!location.matches("\\w+://.+"))
+// location = "file://".concat(location);
+// if (location.toLowerCase(Locale.ENGLISH).startsWith("file://")) {
+// /* Ensure the file exists */
+// File f;
+// try {
+// f = new File(new URI(location));
+// } catch (URISyntaxException e) {
+// return false;
+// } catch (IllegalArgumentException e) {
+// return false;
+// }
+// if (!f.isFile())
+// return false;
+// }
+// return true;
+// }
+//
+// private void showToast(String text, int duration) {
+// Message msg = new Message();
+// Bundle bundle = new Bundle();
+// bundle.putString("text", text);
+// bundle.putInt("duration", duration);
+// msg.setData(bundle);
+// msg.what = SHOW_TOAST;
+// mHandler.sendMessage(msg);
+// }
+//
+// @MainThread
+// public boolean isPlaying() {
+// return mMediaPlayer.isPlaying();
+// }
+//
+// @MainThread
+// public boolean isSeekable() {
+// return mSeekable;
+// }
+//
+// @MainThread
+// public boolean isPausable() {
+// return mPausable;
+// }
+//
+// @MainThread
+// public boolean isShuffling() {
+// return mShuffling;
+// }
+//
+// @MainThread
+// public int getRepeatType() {
+// return mRepeating;
+// }
+//
+// @MainThread
+// public boolean hasMedia() {
+// return hasCurrentMedia();
+// }
+//
+// @MainThread
+// public boolean isVideoPlaying() {
+// return mMediaPlayer.getVLCVout().areViewsAttached();
+// }
+//
+// @MainThread
+// public String getAlbum() {
+// if (hasCurrentMedia())
+// return MediaUtils.getMediaAlbum(PlaybackService.this, getCurrentMedia());
+// else
+// return null;
+// }
+//
+// @MainThread
+// public String getArtist() {
+// if (hasCurrentMedia()) {
+// final MediaWrapper media = getCurrentMedia();
+// return media.getNowPlaying() != null ?
+// media.getTitle()
+// : MediaUtils.getMediaArtist(PlaybackService.this, media);
+// } else
+// return null;
+// }
+//
+// @MainThread
+// public String getArtistPrev() {
+// if (mPrevIndex != -1)
+// return MediaUtils.getMediaArtist(PlaybackService.this, mMediaList.getMedia(mPrevIndex));
+// else
+// return null;
+// }
+//
+// @MainThread
+// public String getArtistNext() {
+// if (mNextIndex != -1)
+// return MediaUtils.getMediaArtist(PlaybackService.this, mMediaList.getMedia(mNextIndex));
+// else
+// return null;
+// }
+//
+// @MainThread
+// public String getTitle() {
+// if (hasCurrentMedia())
+// return getCurrentMedia().getNowPlaying() != null ? getCurrentMedia().getNowPlaying() : getCurrentMedia().getTitle();
+// else
+// return null;
+// }
+//
+// @MainThread
+// public String getTitlePrev() {
+// if (mPrevIndex != -1)
+// return mMediaList.getMedia(mPrevIndex).getTitle();
+// else
+// return null;
+// }
+//
+// @MainThread
+// public String getTitleNext() {
+// if (mNextIndex != -1)
+// return mMediaList.getMedia(mNextIndex).getTitle();
+// else
+// return null;
+// }
+//
+// @MainThread
+// public Bitmap getCover() {
+// if (hasCurrentMedia()) {
+// return AudioUtil.getCover(PlaybackService.this, getCurrentMedia(), 512);
+// }
+// return null;
+// }
+//
+// @MainThread
+// public Bitmap getCoverPrev() {
+// if (mPrevIndex != -1)
+// return AudioUtil.getCover(PlaybackService.this, mMediaList.getMedia(mPrevIndex), 64);
+// else
+// return null;
+// }
+//
+// @MainThread
+// public Bitmap getCoverNext() {
+// if (mNextIndex != -1)
+// return AudioUtil.getCover(PlaybackService.this, mMediaList.getMedia(mNextIndex), 64);
+// else
+// return null;
+// }
+//
+// @MainThread
+// public synchronized void addCallback(Callback cb) {
+// if (!mCallbacks.contains(cb)) {
+// mCallbacks.add(cb);
+// if (hasCurrentMedia())
+// mHandler.sendEmptyMessage(SHOW_PROGRESS);
+// }
+// }
+//
+// @MainThread
+// public synchronized void removeCallback(Callback cb) {
+// mCallbacks.remove(cb);
+// }
+//
+// @MainThread
+// public long getTime() {
+// return mMediaPlayer.getTime();
+// }
+//
+// @MainThread
+// public long getLength() {
+// return mMediaPlayer.getLength();
+// }
+//
+// /**
+// * Loads a selection of files (a non-user-supplied collection of media)
+// * into the primary or "currently playing" playlist.
+// *
+// * @param mediaPathList A list of locations to load
+// * @param position The position to start playing at
+// */
+// @MainThread
+// public void loadLocations(List mediaPathList, int position) {
+// ArrayList mediaList = new ArrayList();
+// MediaDatabase db = MediaDatabase.getInstance();
+//
+// for (int i = 0; i < mediaPathList.size(); i++) {
+// String location = mediaPathList.get(i);
+// MediaWrapper mediaWrapper = db.getMedia(Uri.parse(location));
+// if (mediaWrapper == null) {
+// if (!validateLocation(location)) {
+// Log.w(TAG, "Invalid location " + location);
+// showToast(getResources().getString(R.string.invalid_location, location), Toast.LENGTH_SHORT);
+// continue;
+// }
+// Log.v(TAG, "Creating on-the-fly Media object for " + location);
+// mediaWrapper = new MediaWrapper(Uri.parse(location));
+// }
+// mediaList.add(mediaWrapper);
+// }
+// load(mediaList, position);
+// }
+//
+// @MainThread
+// public void loadUri(Uri uri) {
+// String path = uri.toString();
+// if (TextUtils.equals(uri.getScheme(), "content")) {
+// path = "file://"+ FileUtils.getPathFromURI(uri);
+// }
+// loadLocation(path);
+// }
+//
+// @MainThread
+// public void loadLocation(String mediaPath) {
+// loadLocations(Collections.singletonList(mediaPath), 0);
+// }
+//
+// @MainThread
+// public void load(List mediaList, int position) {
+// Log.v(TAG, "Loading position " + ((Integer) position).toString() + " in " + mediaList.toString());
+//
+// if (hasCurrentMedia())
+// savePosition();
+//
+// mMediaList.removeEventListener(mListEventListener);
+// mMediaList.clear();
+// MediaWrapperList currentMediaList = mMediaList;
+//
+// mPrevious.clear();
+//
+// for (int i = 0; i < mediaList.size(); i++) {
+// currentMediaList.add(mediaList.get(i));
+// }
+//
+// if (mMediaList.size() == 0) {
+// Log.w(TAG, "Warning: empty media list, nothing to play !");
+// return;
+// }
+// if (mMediaList.size() > position && position >= 0) {
+// mCurrentIndex = position;
+// } else {
+// Log.w(TAG, "Warning: positon " + position + " out of bounds");
+// mCurrentIndex = 0;
+// }
+//
+// // Add handler after loading the list
+// mMediaList.addEventListener(mListEventListener);
+//
+// playIndex(mCurrentIndex, 0);
+// saveMediaList();
+// onMediaChanged();
+// }
+//
+// @MainThread
+// public void load(MediaWrapper media) {
+// ArrayList arrayList = new ArrayList();
+// arrayList.add(media);
+// load(arrayList, 0);
+// }
+//
+// /**
+// * Play a media from the media list (playlist)
+// *
+// * @param index The index of the media
+// * @param flags LibVLC.MEDIA_* flags
+// */
+// public void playIndex(int index, int flags) {
+// if (mMediaList.size() == 0) {
+// Log.w(TAG, "Warning: empty media list, nothing to play !");
+// return;
+// }
+// if (index >= 0 && index < mMediaList.size()) {
+// mCurrentIndex = index;
+// } else {
+// Log.w(TAG, "Warning: index " + index + " out of bounds");
+// mCurrentIndex = 0;
+// }
+//
+// String mrl = mMediaList.getMRL(index);
+// if (mrl == null)
+// return;
+// final MediaWrapper mw = mMediaList.getMedia(index);
+// if (mw == null)
+// return;
+// if (mw.getType() == MediaWrapper.TYPE_VIDEO && isVideoPlaying())
+// mw.addFlags(MediaWrapper.MEDIA_VIDEO);
+//
+// /* Pausable and seekable are true by default */
+// mParsed = false;
+// mSwitchingToVideo = false;
+// mPausable = mSeekable = true;
+// final Media media = new Media(VLCInstance.get(), mw.getUri());
+// VLCOptions.setMediaOptions(media, this, flags | mw.getFlags());
+// media.setEventListener(mMediaListener);
+// mMediaPlayer.setMedia(media);
+// media.release();
+// if (mw .getType() != MediaWrapper.TYPE_VIDEO || mw.hasFlag(MediaWrapper.MEDIA_FORCE_AUDIO) || isVideoPlaying()) {
+// mMediaPlayer.setEqualizer(VLCOptions.getEqualizer(this));
+// mMediaPlayer.setVideoTitleDisplay(MediaPlayer.Position.Disable, 0);
+// changeAudioFocus(true);
+// mMediaPlayer.setEventListener(mMediaPlayerListener);
+// mMediaPlayer.play();
+// if(mSavedTime != 0l)
+// mMediaPlayer.setTime(mSavedTime);
+// mSavedTime = 0l;
+//
+// notifyTrackChanged();
+// determinePrevAndNextIndices();
+// if (mSettings.getBoolean(PreferencesFragment.PLAYBACK_HISTORY, true))
+// VLCApplication.runBackground(new Runnable() {
+// @Override
+// public void run() {
+// MediaDatabase.getInstance().addHistoryItem(mw);
+// }
+// });
+// } else {//Start VideoPlayer for first video, it will trigger playIndex when ready.
+// VideoPlayerActivity.startOpened(VLCApplication.getAppContext(),
+// getCurrentMediaWrapper().getUri(), mCurrentIndex);
+// }
+// }
+//
+// /**
+// * Use this function to play a media inside whatever MediaList LibVLC is following.
+// *
+// * Unlike load(), it does not import anything into the primary list.
+// */
+// @MainThread
+// public void playIndex(int index) {
+// playIndex(index, 0);
+// }
+//
+// /**
+// * Use this function to show an URI in the audio interface WITHOUT
+// * interrupting the stream.
+// *
+// * Mainly used by VideoPlayerActivity in response to loss of video track.
+// */
+// @MainThread
+// public void showWithoutParse(int index) {
+// String URI = mMediaList.getMRL(index);
+// Log.v(TAG, "Showing index " + index + " with playing URI " + URI);
+// // Show an URI without interrupting/losing the current stream
+//
+// if(URI == null || !mMediaPlayer.isPlaying())
+// return;
+// mCurrentIndex = index;
+//
+// notifyTrackChanged();
+// showNotification();
+// }
+//
+// /**
+// * Append to the current existing playlist
+// */
+// @MainThread
+// public void append(List mediaList) {
+// if (!hasCurrentMedia())
+// {
+// load(mediaList, 0);
+// return;
+// }
+//
+// for (int i = 0; i < mediaList.size(); i++) {
+// MediaWrapper mediaWrapper = mediaList.get(i);
+// mMediaList.add(mediaWrapper);
+// }
+// onMediaListChanged();
+// }
+//
+// @MainThread
+// public void append(MediaWrapper media) {
+// ArrayList arrayList = new ArrayList();
+// arrayList.add(media);
+// append(arrayList);
+// }
+//
+// /**
+// * Move an item inside the playlist.
+// */
+// @MainThread
+// public void moveItem(int positionStart, int positionEnd) {
+// mMediaList.move(positionStart, positionEnd);
+// PlaybackService.this.saveMediaList();
+// Log.d(TAG, "moveItem " + positionStart + " -> " + positionEnd);
+// }
+//
+// @MainThread
+// public void insertItem(int position, MediaWrapper mw) {
+// mMediaList.insert(position, mw);
+// saveMediaList();
+// determinePrevAndNextIndices();
+// }
+//
+//
+// @MainThread
+// public void remove(int position) {
+// mMediaList.remove(position);
+// saveMediaList();
+// determinePrevAndNextIndices();
+// Log.d(TAG, "remove "+position);
+// }
+//
+// @MainThread
+// public void removeLocation(String location) {
+// mMediaList.remove(location);
+// saveMediaList();
+// determinePrevAndNextIndices();
+// }
+//
+// public int getMediaListSize() {
+// return mMediaList.size();
+// }
+//
+// @MainThread
+// public List getMedias() {
+// final ArrayList ml = new ArrayList();
+// for (int i = 0; i < mMediaList.size(); i++) {
+// ml.add(mMediaList.getMedia(i));
+// }
+// return ml;
+// }
+//
+// @MainThread
+// public List getMediaLocations() {
+// ArrayList medias = new ArrayList();
+// for (int i = 0; i < mMediaList.size(); i++) {
+// medias.add(mMediaList.getMRL(i));
+// }
+// return medias;
+// }
+//
+// @MainThread
+// public String getCurrentMediaLocation() {
+// return mMediaList.getMRL(mCurrentIndex);
+// }
+//
+// @MainThread
+// public int getCurrentMediaPosition() {
+// return mCurrentIndex;
+// }
+//
+// @MainThread
+// public MediaWrapper getCurrentMediaWrapper() {
+// return PlaybackService.this.getCurrentMedia();
+// }
+//
+// @MainThread
+// public void setTime(long time) {
+// if (mSeekable)
+// mMediaPlayer.setTime(time);
+// }
+//
+// @MainThread
+// public boolean hasNext() {
+// return mNextIndex != -1;
+// }
+//
+// @MainThread
+// public boolean hasPrevious() {
+// return mPrevIndex != -1;
+// }
+//
+// @MainThread
+// public void detectHeadset(boolean enable) {
+// mDetectHeadset = enable;
+// }
+//
+// @MainThread
+// public float getRate() {
+// return mMediaPlayer.getRate();
+// }
+//
+// @MainThread
+// public void setRate(float rate) {
+// mMediaPlayer.setRate(rate);
+// }
+//
+// @MainThread
+// public void navigate(int where) {
+// mMediaPlayer.navigate(where);
+// }
+//
+// @MainThread
+// public MediaPlayer.Chapter[] getChapters(int title) {
+// return mMediaPlayer.getChapters(title);
+// }
+//
+// @MainThread
+// public MediaPlayer.Title[] getTitles() {
+// return mMediaPlayer.getTitles();
+// }
+//
+// @MainThread
+// public int getChapterIdx() {
+// return mMediaPlayer.getChapter();
+// }
+//
+// @MainThread
+// public void setChapterIdx(int chapter) {
+// mMediaPlayer.setChapter(chapter);
+// }
+//
+// @MainThread
+// public int getTitleIdx() {
+// return mMediaPlayer.getTitle();
+// }
+//
+// @MainThread
+// public void setTitleIdx(int title) {
+// mMediaPlayer.setTitle(title);
+// }
+//
+// @MainThread
+// public int getVolume() {
+// return mMediaPlayer.getVolume();
+// }
+//
+// @MainThread
+// public int setVolume(int volume) {
+// return mMediaPlayer.setVolume(volume);
+// }
+//
+// @MainThread
+// public void setPosition(float pos) {
+// if (mSeekable)
+// mMediaPlayer.setPosition(pos);
+// }
+//
+// @MainThread
+// public int getAudioTracksCount() {
+// return mMediaPlayer.getAudioTracksCount();
+// }
+//
+// @MainThread
+// public MediaPlayer.TrackDescription[] getAudioTracks() {
+// return mMediaPlayer.getAudioTracks();
+// }
+//
+// @MainThread
+// public int getAudioTrack() {
+// return mMediaPlayer.getAudioTrack();
+// }
+//
+// @MainThread
+// public boolean setAudioTrack(int index) {
+// return mMediaPlayer.setAudioTrack(index);
+// }
+//
+// @MainThread
+// public int getVideoTracksCount() {
+// return mMediaPlayer.getVideoTracksCount();
+// }
+//
+// @MainThread
+// public boolean addSubtitleTrack(String path) {
+// return mMediaPlayer.setSubtitleFile(path);
+// }
+//
+// @MainThread
+// public MediaPlayer.TrackDescription[] getSpuTracks() {
+// return mMediaPlayer.getSpuTracks();
+// }
+//
+// @MainThread
+// public int getSpuTrack() {
+// return mMediaPlayer.getSpuTrack();
+// }
+//
+// @MainThread
+// public boolean setSpuTrack(int index) {
+// return mMediaPlayer.setSpuTrack(index);
+// }
+//
+// @MainThread
+// public int getSpuTracksCount() {
+// return mMediaPlayer.getSpuTracksCount();
+// }
+//
+// @MainThread
+// public boolean setAudioDelay(long delay) {
+// return mMediaPlayer.setAudioDelay(delay);
+// }
+//
+// @MainThread
+// public long getAudioDelay() {
+// return mMediaPlayer.getAudioDelay();
+// }
+//
+// @MainThread
+// public boolean setSpuDelay(long delay) {
+// return mMediaPlayer.setSpuDelay(delay);
+// }
+//
+// @MainThread
+// public long getSpuDelay() {
+// return mMediaPlayer.getSpuDelay();
+// }
+//
+// @MainThread
+// public void setEqualizer(MediaPlayer.Equalizer equalizer) {
+// mMediaPlayer.setEqualizer(equalizer);
+// }
+//
+// /**
+// * Expand the current media.
+// * @return the index of the media was expanded, and -1 if no media was expanded
+// */
+// @MainThread
+// public int expand() {
+// final Media media = mMediaPlayer.getMedia();
+// if (media == null)
+// return -1;
+// final MediaList ml = media.subItems();
+// media.release();
+// int ret;
+//
+// if (ml.getCount() > 0) {
+// mMediaList.remove(mCurrentIndex);
+// for (int i = ml.getCount() - 1; i >= 0; --i) {
+// final Media child = ml.getMediaAt(i);
+// child.parse();
+// mMediaList.insert(mCurrentIndex, new MediaWrapper(child));
+// child.release();
+// }
+// ret = 0;
+// } else {
+// ret = -1;
+// }
+// ml.release();
+// return ret;
+// }
+//
+// public void restartMediaPlayer() {
+// stop();
+// mMediaPlayer.release();
+// mMediaPlayer = newMediaPlayer();
+// /* TODO RESUME */
+// }
+//
+// public static class Client {
+// public static final String TAG = "PlaybackService.Client";
+//
+// @MainThread
+// public interface Callback {
+// void onConnected(PlaybackService service);
+// void onDisconnected();
+// }
+//
+// private boolean mBound = false;
+// private final Callback mCallback;
+// private final Context mContext;
+//
+// private final ServiceConnection mServiceConnection = new ServiceConnection() {
+// @Override
+// public void onServiceConnected(ComponentName name, IBinder iBinder) {
+// Log.d(TAG, "Service Connected");
+// if (!mBound)
+// return;
+//
+// final PlaybackService service = PlaybackService.getService(iBinder);
+// if (service != null)
+// mCallback.onConnected(service);
+// }
+//
+// @Override
+// public void onServiceDisconnected(ComponentName name) {
+// Log.d(TAG, "Service Disconnected");
+// mCallback.onDisconnected();
+// }
+// };
+//
+// private static Intent getServiceIntent(Context context) {
+// return new Intent(context, PlaybackService.class);
+// }
+//
+// private static void startService(Context context) {
+// context.startService(getServiceIntent(context));
+// }
+//
+// private static void stopService(Context context) {
+// context.stopService(getServiceIntent(context));
+// }
+//
+// public Client(Context context, Callback callback) {
+// if (context == null || callback == null)
+// throw new IllegalArgumentException("Context and callback can't be null");
+// mContext = context;
+// mCallback = callback;
+// }
+//
+// @MainThread
+// public void connect() {
+// if (mBound)
+// throw new IllegalStateException("already connected");
+// startService(mContext);
+// mBound = mContext.bindService(getServiceIntent(mContext), mServiceConnection, BIND_AUTO_CREATE);
+// }
+//
+// @MainThread
+// public void disconnect() {
+// if (mBound) {
+// mBound = false;
+// mContext.unbindService(mServiceConnection);
+// }
+// }
+//
+// public static void restartService(Context context) {
+// stopService(context);
+// startService(context);
+// }
+// }
+//
+// int mPhoneEvents = PhoneStateListener.LISTEN_CALL_STATE;
+// PhoneStateListener mPhoneStateListener;
+//
+// private void initPhoneListener() {
+// mPhoneStateListener = new PhoneStateListener(){
+// @Override
+// public void onCallStateChanged(int state, String incomingNumber) {
+// if (!mMediaPlayer.isPlaying() || !hasCurrentMedia())
+// return;
+// if (state == TelephonyManager.CALL_STATE_RINGING || state == TelephonyManager.CALL_STATE_OFFHOOK)
+// pause();
+// else if (state == TelephonyManager.CALL_STATE_IDLE)
+// play();
+// }
+// };
+// }
+//}
diff --git a/app/src/main/java/org/stepic/droid/util/AndroidDevices.java b/app/src/main/java/org/stepic/droid/util/AndroidDevices.java
new file mode 100644
index 0000000000..7431a7a4b6
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/util/AndroidDevices.java
@@ -0,0 +1,169 @@
+/*****************************************************************************
+ * AndroidDevices.java
+ *****************************************************************************
+ * Copyright © 2011-2014 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+package org.stepic.droid.util;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.os.Environment;
+import android.telephony.TelephonyManager;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+
+import org.stepic.droid.base.MainApplication;
+import org.videolan.libvlc.util.AndroidUtil;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public class AndroidDevices {
+ public final static String TAG = "VLC/UiTools/AndroidDevices";
+ public final static String EXTERNAL_PUBLIC_DIRECTORY = Environment.getExternalStorageDirectory().getPath();
+
+ final static boolean hasNavBar;
+ final static boolean hasTsp, isTv;
+
+ static {
+ HashSet devicesWithoutNavBar = new HashSet();
+ devicesWithoutNavBar.add("HTC One V");
+ devicesWithoutNavBar.add("HTC One S");
+ devicesWithoutNavBar.add("HTC One X");
+ devicesWithoutNavBar.add("HTC One XL");
+ hasNavBar = AndroidUtil.isICSOrLater()
+ && !devicesWithoutNavBar.contains(android.os.Build.MODEL);
+ hasTsp = MainApplication.getAppContext().getPackageManager().hasSystemFeature("android.hardware.touchscreen");
+ isTv = MainApplication.getAppContext().getPackageManager().hasSystemFeature("android.software.leanback");
+ }
+
+ public static boolean hasExternalStorage() {
+ return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
+ }
+
+ public static boolean hasNavBar() {
+ return hasNavBar;
+ }
+
+ /**
+ * hasCombBar test if device has Combined Bar : only for tablet with Honeycomb or ICS
+ */
+ public static boolean hasCombBar() {
+ return (!AndroidDevices.isPhone()
+ && ((VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) &&
+ (VERSION.SDK_INT <= VERSION_CODES.JELLY_BEAN)));
+ }
+
+ public static boolean isPhone() {
+ TelephonyManager manager = (TelephonyManager) MainApplication.getAppContext().getSystemService(Context.TELEPHONY_SERVICE);
+ return manager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
+ }
+
+ public static boolean hasTsp() {
+ return hasTsp;
+ }
+
+ public static boolean isAndroidTv() {
+ return isTv;
+ }
+
+ public static ArrayList getStorageDirectories() {
+ BufferedReader bufReader = null;
+ ArrayList list = new ArrayList();
+ list.add(EXTERNAL_PUBLIC_DIRECTORY);
+
+ List typeWL = Arrays.asList("vfat", "exfat", "sdcardfs", "fuse", "ntfs", "fat32", "ext3", "ext4", "esdfs");
+ List typeBL = Arrays.asList("tmpfs");
+ String[] mountWL = {"/mnt", "/Removable", "/storage"};
+ String[] mountBL = {
+ "/mnt/secure",
+ "/mnt/shell",
+ "/mnt/asec",
+ "/mnt/obb",
+ "/mnt/media_rw/extSdCard",
+ "/mnt/media_rw/sdcard",
+ "/storage/emulated"};
+ String[] deviceWL = {
+ "/dev/block/vold",
+ "/dev/fuse",
+ "/mnt/media_rw"};
+
+ try {
+ bufReader = new BufferedReader(new FileReader("/proc/mounts"));
+ String line;
+ while ((line = bufReader.readLine()) != null) {
+
+ StringTokenizer tokens = new StringTokenizer(line, " ");
+ String device = tokens.nextToken();
+ String mountpoint = tokens.nextToken();
+ String type = tokens.nextToken();
+
+ // skip if already in list or if type/mountpoint is blacklisted
+ if (list.contains(mountpoint) || typeBL.contains(type) || Strings.startsWith(mountBL, mountpoint))
+ continue;
+
+ // check that device is in whitelist, and either type or mountpoint is in a whitelist
+ if (Strings.startsWith(deviceWL, device) && (typeWL.contains(type) || Strings.startsWith(mountWL, mountpoint))) {
+ int position = Strings.containsName(list, FileUtils.getFileNameFromPath(mountpoint));
+ if (position > -1)
+ list.remove(position);
+ list.add(mountpoint);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ } catch (IOException e) {
+ } finally {
+ Util.close(bufReader);
+ }
+ return list;
+ }
+
+
+
+ @TargetApi(VERSION_CODES.HONEYCOMB_MR1)
+ public static float getCenteredAxis(MotionEvent event,
+ InputDevice device, int axis) {
+ final InputDevice.MotionRange range =
+ device.getMotionRange(axis, event.getSource());
+
+ // A joystick at rest does not always report an absolute position of
+ // (0,0). Use the getFlat() method to determine the range of values
+ // bounding the joystick axis center.
+ if (range != null) {
+ final float flat = range.getFlat();
+ final float value = event.getAxisValue(axis);
+
+ // Ignore axis values that are within the 'flat' region of the
+ // joystick axis center.
+ if (Math.abs(value) > flat) {
+ return value;
+ }
+ }
+ return 0;
+ }
+
+}
diff --git a/app/src/main/java/org/stepic/droid/util/AppConstants.java b/app/src/main/java/org/stepic/droid/util/AppConstants.java
index 08705c2602..68d26fda7a 100644
--- a/app/src/main/java/org/stepic/droid/util/AppConstants.java
+++ b/app/src/main/java/org/stepic/droid/util/AppConstants.java
@@ -9,7 +9,7 @@ public class AppConstants {
public static final String KEY_UNIT_BUNDLE = "unit";
public static final String KEY_LESSON_BUNDLE = "lesson";
public static final String KEY_STEP_BUNDLE = "step";
- public static final String DEFAULT_QUALITY = "270";
+ public static final String DEFAULT_QUALITY = "360";
public static final String PRE_BODY = "\n" +
"\n" +
"Step. Stepic.org\n" +
diff --git a/app/src/main/java/org/stepic/droid/util/FileUtils.java b/app/src/main/java/org/stepic/droid/util/FileUtils.java
new file mode 100644
index 0000000000..9853baa1f4
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/util/FileUtils.java
@@ -0,0 +1,192 @@
+/*
+ * *************************************************************************
+ * FileUtils.java
+ * **************************************************************************
+ * Copyright © 2015 VLC authors and VideoLAN
+ * Author: Geoffrey Métais
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ * ***************************************************************************
+ */
+
+package org.stepic.droid.util;
+
+import android.annotation.TargetApi;
+import android.content.ContentResolver;
+import android.content.res.AssetManager;
+import android.net.Uri;
+import android.os.Build;
+import android.provider.MediaStore;
+import android.text.TextUtils;
+
+import org.stepic.droid.base.MainApplication;
+import org.videolan.libvlc.util.AndroidUtil;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class FileUtils {
+
+ public interface Callback {
+ void onResult(boolean success);
+ }
+
+ public static String getFileNameFromPath(String path){
+ if (path == null)
+ return "";
+ int index = path.lastIndexOf('/');
+ if (index> -1)
+ return path.substring(index+1);
+ else
+ return path;
+ }
+
+ public static String getParent(String path){
+ if (TextUtils.equals("/", path))
+ return path;
+ String parentPath = path;
+ if (parentPath.endsWith("/"))
+ parentPath = parentPath.substring(0, parentPath.length()-1);
+ int index = parentPath.lastIndexOf('/');
+ if (index > 0){
+ parentPath = parentPath.substring(0, index);
+ } else if (index == 0)
+ parentPath = "/";
+ return parentPath;
+ }
+
+ public static boolean copyAssetFolder(AssetManager assetManager, String fromAssetPath, String toPath) {
+ try {
+ String[] files = assetManager.list(fromAssetPath);
+ if (files.length == 0)
+ return false;
+ new File(toPath).mkdirs();
+ boolean res = true;
+ for (String file : files)
+ if (file.contains("."))
+ res &= copyAsset(assetManager,
+ fromAssetPath + "/" + file,
+ toPath + "/" + file);
+ else
+ res &= copyAssetFolder(assetManager,
+ fromAssetPath + "/" + file,
+ toPath + "/" + file);
+ return res;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ private static boolean copyAsset(AssetManager assetManager,
+ String fromAssetPath, String toPath) {
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ in = assetManager.open(fromAssetPath);
+ new File(toPath).createNewFile();
+ out = new FileOutputStream(toPath);
+ copyFile(in, out);
+ out.flush();
+ return true;
+ } catch(Exception e) {
+ e.printStackTrace();
+ return false;
+ } finally {
+ Util.close(in);
+ Util.close(out);
+ }
+ }
+
+ public static void copyFile(InputStream in, OutputStream out) throws IOException {
+ byte[] buffer = new byte[1024];
+ int read;
+ while((read = in.read(buffer)) != -1){
+ out.write(buffer, 0, read);
+ }
+ }
+
+ public static boolean copyFile(File src, File dst){
+ boolean ret = true;
+ if (src.isDirectory()) {
+ File[] filesList = src.listFiles();
+ dst.mkdirs();
+ for (File file : filesList)
+ ret &= copyFile(file, new File(dst, file.getName()));
+ } else if (src.isFile()) {
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ in = new BufferedInputStream(new FileInputStream(src));
+ out = new BufferedOutputStream(new FileOutputStream(dst));
+
+ // Transfer bytes from in to out
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = in.read(buf)) > 0) {
+ out.write(buf, 0, len);
+ }
+ return true;
+ } catch (FileNotFoundException e) {
+ } catch (IOException e) {
+ } finally {
+ Util.close(in);
+ Util.close(out);
+ }
+ return false;
+ }
+ return ret;
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public static boolean deleteFile (String path){
+ boolean deleted = false;
+ path = Uri.decode(Strings.removeFileProtocol(path));
+ //Delete from Android Medialib, for consistency with device MTP storing and other apps listing content:// media
+ if (AndroidUtil.isHoneycombOrLater()){
+ ContentResolver cr = MainApplication.getAppContext().getContentResolver();
+ String[] selectionArgs = { path };
+ deleted = cr.delete(MediaStore.Files.getContentUri("external"),
+ MediaStore.Files.FileColumns.DATA + "=?", selectionArgs) > 0;
+ }
+ File file = new File(path);
+ if (file.exists())
+ deleted |= file.delete();
+ return deleted;
+ }
+
+
+ public static boolean canWrite(String path){
+ if (path == null)
+ return false;
+ if (path.startsWith("file://"))
+ path = path.substring(7);
+ if (!path.startsWith("/"))
+ return false;
+ if (path.startsWith(AndroidDevices.EXTERNAL_PUBLIC_DIRECTORY))
+ return true;
+ if (AndroidUtil.isKitKatOrLater())
+ return false;
+ File file = new File(path);
+ return (file.exists() && file.canWrite());
+ }
+}
diff --git a/app/src/main/java/org/stepic/droid/util/Strings.java b/app/src/main/java/org/stepic/droid/util/Strings.java
new file mode 100644
index 0000000000..6ea03caa07
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/util/Strings.java
@@ -0,0 +1,135 @@
+/*****************************************************************************
+ * Strings.java
+ * ****************************************************************************
+ * Copyright © 2011-2014 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+package org.stepic.droid.util;
+
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.List;
+import java.util.Locale;
+
+public class Strings {
+ public final static String TAG = "VLC/UiTools/Strings";
+
+ public static String stripTrailingSlash(String s) {
+ if (s.endsWith("/") && s.length() > 1)
+ return s.substring(0, s.length() - 1);
+ return s;
+ }
+
+ static boolean startsWith(String[] array, String text) {
+ for (String item : array)
+ if (text.startsWith(item))
+ return true;
+ return false;
+ }
+
+ static int containsName(List array, String text) {
+ for (int i = array.size() - 1; i >= 0; --i)
+ if (array.get(i).endsWith(text))
+ return i;
+ return -1;
+ }
+
+ /**
+ * Convert time to a string
+ * @param millis e.g.time/length from file
+ * @return formated string (hh:)mm:ss
+ */
+ public static String millisToString(long millis) {
+ return Strings.millisToString(millis, false);
+ }
+
+ /**
+ * Convert time to a string
+ * @param millis e.g.time/length from file
+ * @return formated string "[hh]h[mm]min" / "[mm]min[s]s"
+ */
+ public static String millisToText(long millis) {
+ return Strings.millisToString(millis, true);
+ }
+
+ static String millisToString(long millis, boolean text) {
+ boolean negative = millis < 0;
+ millis = java.lang.Math.abs(millis);
+
+ millis /= 1000;
+ int sec = (int) (millis % 60);
+ millis /= 60;
+ int min = (int) (millis % 60);
+ millis /= 60;
+ int hours = (int) millis;
+
+ String time;
+ DecimalFormat format = (DecimalFormat) NumberFormat.getInstance(Locale.US);
+ format.applyPattern("00");
+ if (text) {
+ if (millis > 0)
+ time = (negative ? "-" : "") + hours + "h" + format.format(min) + "min";
+ else if (min > 0)
+ time = (negative ? "-" : "") + min + "min";
+ else
+ time = (negative ? "-" : "") + sec + "s";
+ } else {
+ if (millis > 0)
+ time = (negative ? "-" : "") + hours + ":" + format.format(min) + ":" + format.format(sec);
+ else
+ time = (negative ? "-" : "") + min + ":" + format.format(sec);
+ }
+ return time;
+ }
+
+ /**
+ * equals() with two strings where either could be null
+ */
+ public static boolean nullEquals(String s1, String s2) {
+ return (s1 == null ? s2 == null : s1.equals(s2));
+ }
+
+ /**
+ * Get the formatted current playback speed in the form of 1.00x
+ */
+ public static String formatRateString(float rate) {
+ return String.format(java.util.Locale.US, "%.2fx", rate);
+ }
+
+ public static String readableFileSize(long size) {
+ if (size <= 0) return "0";
+ final String[] units = new String[]{"B", "KiB", "MiB", "GiB", "TiB"};
+ int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
+ return new DecimalFormat("#,##0.#").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
+ }
+
+ public static String readableSize(long size) {
+ if (size <= 0) return "0";
+ final String[] units = new String[]{"B", "KB", "MB", "GB", "TB"};
+ int digitGroups = (int) (Math.log10(size) / Math.log10(1000));
+ return new DecimalFormat("#,##0.#").format(size / Math.pow(1000, digitGroups)) + " " + units[digitGroups];
+ }
+
+ public static String removeFileProtocol(String path) {
+ if (path == null)
+ return null;
+ if (path.startsWith("file://"))
+ return path.substring(7);
+ else
+ return path;
+ }
+}
diff --git a/app/src/main/java/org/stepic/droid/util/TimeUtil.java b/app/src/main/java/org/stepic/droid/util/TimeUtil.java
new file mode 100644
index 0000000000..a397e4880e
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/util/TimeUtil.java
@@ -0,0 +1,24 @@
+package org.stepic.droid.util;
+
+public class TimeUtil {
+ public static String getFormattedVideoTime(long milliseconds) {
+ long seconds = milliseconds / 1000;
+ long minutes = seconds / 60;
+ seconds = seconds % 60;
+
+ return getRepresentation(minutes) + " : " + getRepresentation(seconds);
+ }
+
+ private static String getRepresentation(long number) {
+
+ if (number == 0) {
+ return "00";
+ }
+
+ if (number / 10 == 0) {
+ return "0" + number;
+ }
+
+ return String.valueOf(number);
+ }
+}
diff --git a/app/src/main/java/org/stepic/droid/util/Util.java b/app/src/main/java/org/stepic/droid/util/Util.java
new file mode 100644
index 0000000000..f28a6a5594
--- /dev/null
+++ b/app/src/main/java/org/stepic/droid/util/Util.java
@@ -0,0 +1,90 @@
+/*****************************************************************************
+ * UiTools.java
+ *****************************************************************************
+ * Copyright © 2011-2014 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+package org.stepic.droid.util;
+
+import android.annotation.TargetApi;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+
+import org.stepic.droid.base.MainApplication;
+import org.videolan.libvlc.util.AndroidUtil;
+
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.List;
+
+public class Util {
+ public final static String TAG = "VLC/Util";
+
+ public static String readAsset(String assetName, String defaultS) {
+ InputStream is = null;
+ BufferedReader r = null;
+ try {
+ is = MainApplication.getAppContext().getResources().getAssets().open(assetName);
+ r = new BufferedReader(new InputStreamReader(is, "UTF8"));
+ StringBuilder sb = new StringBuilder();
+ String line = r.readLine();
+ if(line != null) {
+ sb.append(line);
+ line = r.readLine();
+ while(line != null) {
+ sb.append('\n');
+ sb.append(line);
+ line = r.readLine();
+ }
+ }
+ return sb.toString();
+ } catch (IOException e) {
+ return defaultS;
+ } finally {
+ close(is);
+ close(r);
+ }
+ }
+
+ @TargetApi(android.os.Build.VERSION_CODES.GINGERBREAD)
+ public static void commitPreferences(SharedPreferences.Editor editor){
+ if (AndroidUtil.isGingerbreadOrLater())
+ editor.apply();
+ else
+ editor.commit();
+ }
+
+ public static boolean close(Closeable closeable) {
+ if (closeable != null)
+ try {
+ closeable.close();
+ return true;
+ } catch (IOException e) {}
+ return false;
+ }
+
+ public static boolean isCallable(Intent intent) {
+ List list = MainApplication.getAppContext().getPackageManager().queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ return list.size() > 0;
+ }
+}
diff --git a/app/src/main/java/org/stepic/droid/util/resolvers/VideoResolver.java b/app/src/main/java/org/stepic/droid/util/resolvers/VideoResolver.java
index 3505a57c2e..e83bc0a2b5 100644
--- a/app/src/main/java/org/stepic/droid/util/resolvers/VideoResolver.java
+++ b/app/src/main/java/org/stepic/droid/util/resolvers/VideoResolver.java
@@ -55,13 +55,14 @@ public String resolveVideoUrl(@Nullable final Video video) {
private String resolveFromWeb(List urlList) {
String resolvedURL = null;
- for (int i = 0; i < urlList.size(); i++) {
+ if (urlList == null || urlList.isEmpty()) return null;
+ int upperBound = urlList.size() - 1;
+ for (int i = upperBound; i >= 0; i--) {
VideoUrl tempLink = urlList.get(i);
if (tempLink != null) {
String quality = tempLink.getQuality();
if (quality != null &&
- (quality.equals(mUserPreferences.getQualityVideo()) || i == urlList.size() - 1)) {
- //// TODO: 15.10.15 determine video which is available for the phone. Not default
+ (quality.equals(mUserPreferences.getQualityVideo()) || i == 0)) {
resolvedURL = tempLink.getUrl();
break;
}
diff --git a/app/src/main/java/org/stepic/droid/view/activities/MainFeedActivity.java b/app/src/main/java/org/stepic/droid/view/activities/MainFeedActivity.java
index a9857e9df3..505bd6a55f 100644
--- a/app/src/main/java/org/stepic/droid/view/activities/MainFeedActivity.java
+++ b/app/src/main/java/org/stepic/droid/view/activities/MainFeedActivity.java
@@ -1,5 +1,6 @@
package org.stepic.droid.view.activities;
+import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
@@ -71,14 +72,14 @@ public class MainFeedActivity extends FragmentActivityBase
private int mCurrentIndex;
-// @Override
-// protected void onNewIntent(Intent intent) {
-// super.onNewIntent(intent);
-// Bundle extras = intent.getExtras();
-// if (extras != null){
-// initFragments(extras);
-// }
-// }
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ Bundle extras = intent.getExtras();
+ if (extras != null) {
+ initFragments(extras);
+ }
+ }
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -268,7 +269,16 @@ private void setFragment(MenuItem menuItem) {
}
mCurrentIndex--; // menu indices from 1
if (shortLifetimeRef != null) {
- setFragment(R.id.frame, shortLifetimeRef);
+ Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.frame);
+ if (fragment != null) {
+ String before = fragment.getTag();
+ String now = shortLifetimeRef.getClass().toString();
+ if (!before.equals(now)) {
+ setFragment(R.id.frame, shortLifetimeRef);
+ }
+ } else {
+ setFragment(R.id.frame, shortLifetimeRef);
+ }
}
}
diff --git a/app/src/main/java/org/stepic/droid/view/activities/SectionActivity.java b/app/src/main/java/org/stepic/droid/view/activities/SectionActivity.java
index 5e31b17117..d7d28d41d4 100644
--- a/app/src/main/java/org/stepic/droid/view/activities/SectionActivity.java
+++ b/app/src/main/java/org/stepic/droid/view/activities/SectionActivity.java
@@ -18,8 +18,8 @@
import org.stepic.droid.R;
import org.stepic.droid.base.FragmentActivityBase;
-import org.stepic.droid.concurrency.FromDbSectionTask;
-import org.stepic.droid.concurrency.ToDbSectionTask;
+import org.stepic.droid.concurrency.tasks.FromDbSectionTask;
+import org.stepic.droid.concurrency.tasks.ToDbSectionTask;
import org.stepic.droid.events.notify_ui.NotifyUISectionsEvent;
import org.stepic.droid.events.sections.FailureResponseSectionEvent;
import org.stepic.droid.events.sections.FinishingGetSectionFromDbEvent;
@@ -64,8 +64,6 @@ public class SectionActivity extends FragmentActivityBase implements SwipeRefres
private Course mCourse;
private SectionAdapter mAdapter;
private List mSectionList;
- private FromDbSectionTask mFromDbSectionTask;
- private ToDbSectionTask mToDbSectionTask;
boolean isScreenEmpty;
boolean firstLoad;
@@ -143,8 +141,8 @@ public void onFailure(Throwable t) {
}
private void getAndShowSectionsFromCache() {
- mFromDbSectionTask = new FromDbSectionTask(mCourse);
- mFromDbSectionTask.execute();
+ FromDbSectionTask fromDbSectionTask = new FromDbSectionTask(mCourse);
+ fromDbSectionTask.executeOnExecutor(mThreadPoolExecutor);
}
private void showSections(List sections) {
@@ -184,8 +182,8 @@ public void onRefresh() {
private void saveDataToCache(List sections) {
- mToDbSectionTask = new ToDbSectionTask(sections);
- mToDbSectionTask.execute();
+ ToDbSectionTask toDbSectionTask = new ToDbSectionTask(sections);
+ toDbSectionTask.executeOnExecutor(mThreadPoolExecutor);
}
@Subscribe
diff --git a/app/src/main/java/org/stepic/droid/view/activities/UnitsActivity.java b/app/src/main/java/org/stepic/droid/view/activities/UnitsActivity.java
index 8ac1322bf0..cfa6e6290d 100644
--- a/app/src/main/java/org/stepic/droid/view/activities/UnitsActivity.java
+++ b/app/src/main/java/org/stepic/droid/view/activities/UnitsActivity.java
@@ -18,8 +18,8 @@
import org.jetbrains.annotations.Nullable;
import org.stepic.droid.R;
import org.stepic.droid.base.FragmentActivityBase;
-import org.stepic.droid.concurrency.FromDbUnitLessonTask;
-import org.stepic.droid.concurrency.ToDbUnitLessonTask;
+import org.stepic.droid.concurrency.tasks.FromDbUnitLessonTask;
+import org.stepic.droid.concurrency.tasks.ToDbUnitLessonTask;
import org.stepic.droid.events.lessons.SuccessLoadLessonsEvent;
import org.stepic.droid.events.notify_ui.NotifyUIUnitLessonEvent;
import org.stepic.droid.events.units.FailureLoadEvent;
@@ -118,7 +118,7 @@ protected void onCreate(Bundle savedInstanceState) {
private void getAndShowUnitsFromCache() {
mFromDbTask = new FromDbUnitLessonTask(mSection);
- mFromDbTask.execute();
+ mFromDbTask.executeOnExecutor(mThreadPoolExecutor);
}
@@ -197,7 +197,7 @@ public void onFailure(Throwable t) {
private void saveToDb(List unitList, List lessonList, List