diff --git a/app/build.gradle b/app/build.gradle index 42cf708..d244f8c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -70,10 +70,9 @@ dependencies { implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation 'com.google.android.material:material:1.1.0' implementation 'com.google.android.exoplayer:exoplayer:2.11.3' - //noinspection GradleDependency - implementation 'com.squareup.okhttp3:okhttp:3.12.2' implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.github.ybq:Android-SpinKit:1.4.0' + implementation 'com.android.volley:volley:1.1.1' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' diff --git a/app/src/main/java/net/harimurti/tv/MainActivity.java b/app/src/main/java/net/harimurti/tv/MainActivity.java index 11481a0..722f104 100644 --- a/app/src/main/java/net/harimurti/tv/MainActivity.java +++ b/app/src/main/java/net/harimurti/tv/MainActivity.java @@ -1,33 +1,46 @@ package net.harimurti.tv; import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.FragmentActivity; import androidx.viewpager2.widget.ViewPager2; +import android.os.Build; +import android.os.Build.VERSION_CODES; import android.os.Bundle; +import android.util.Log; import android.view.View; import android.widget.TextView; +import com.android.volley.Request; +import com.android.volley.RequestQueue; +import com.android.volley.toolbox.BaseHttpStack; +import com.android.volley.toolbox.HurlStack; +import com.android.volley.toolbox.StringRequest; +import com.android.volley.toolbox.Volley; import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayoutMediator; +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; import net.harimurti.tv.adapter.ViewPagerAdapter; import net.harimurti.tv.data.Playlist; import net.harimurti.tv.extra.AsyncSleep; import net.harimurti.tv.extra.Network; -import net.harimurti.tv.extra.RestClient; -import net.harimurti.tv.extra.RestClient.OnClientResult; +import net.harimurti.tv.extra.TLSSocketFactory; + +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; public class MainActivity extends AppCompatActivity { - private RestClient client; private View layoutStatus, layoutSpin, layoutText; private TextView tvStatus, tvRetry; + private StringRequest request; + private RequestQueue volley; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - FragmentActivity fa = this; // define some view TabLayout tabLayout = findViewById(R.id.tab_layout); @@ -38,71 +51,78 @@ protected void onCreate(Bundle savedInstanceState) { tvStatus = findViewById(R.id.text_status); tvRetry = findViewById(R.id.text_retry); - // create client & set listener -> getchannels - client = new RestClient(this); - client.setOnClientResult(new OnClientResult() { - @Override - public void onFailure(String status) { - runOnUiThread(() -> { - ShowLayoutMessage(View.VISIBLE, true); - tvStatus.setText(status); - - // try fetching later - RetryGetChannels(); - }); - } + request = new StringRequest(Request.Method.GET, + getString(R.string.json_playlist), + response -> { + try { + Playlist playlist = new Gson().fromJson(response, Playlist.class); + viewPager.setAdapter(new ViewPagerAdapter(this, playlist)); + new TabLayoutMediator( + tabLayout, viewPager, (tab, i) -> tab.setText(playlist.categories.get(i).name) + ).attach(); + ShowLayoutMessage(View.GONE, false); + } catch (JsonSyntaxException error) { + ShowErrorMessage(error.getMessage(), false); + } + }, + error -> ShowErrorMessage(error.getMessage(), true)); - @Override - public void onProgress(boolean status) { - runOnUiThread(() -> { - ShowLayoutMessage(status ? View.VISIBLE : View.GONE, false); - }); + BaseHttpStack stack = new HurlStack(); + if (Build.VERSION.SDK_INT == VERSION_CODES.KITKAT) { + try { + stack = new HurlStack(null, new TLSSocketFactory()); + } catch (KeyManagementException | NoSuchAlgorithmException e) { + Log.e("Volley", "Could not create new stack for TLS v1.2"); } + } + volley = Volley.newRequestQueue(this, stack); + volley.add(request); + } - @Override - public void onSuccess(Playlist playlist) { - runOnUiThread(() -> { - viewPager.setAdapter(new ViewPagerAdapter(fa, playlist)); - new TabLayoutMediator( - tabLayout, viewPager, (tab, i) -> tab.setText(playlist.categories.get(i).name) - ).attach(); - }); - } - }); - client.GetChannels(); + private void ShowLayoutMessage(int visibility, boolean isMessage) { + layoutStatus.setVisibility(visibility); + if (!isMessage) { + layoutSpin.setVisibility(View.VISIBLE); + layoutText.setVisibility(View.GONE); + } else { + layoutSpin.setVisibility(View.GONE); + layoutText.setVisibility(View.VISIBLE); + } } - private void RetryGetChannels() { + private void ShowErrorMessage(String error, boolean retry) { + tvStatus.setText(error); + tvRetry.setText(R.string.text_auto_retry); + ShowLayoutMessage(View.VISIBLE, true); + + if (!retry) return; + Network network = new Network(this); new AsyncSleep(this).task(new AsyncSleep.Task() { @Override public void onCountDown(int left) { - if (!network.IsConnected()) - tvStatus.setText(getString(R.string.no_network)); - - tvRetry.setText(String.format(getString(R.string.retry_time), left)); + if (!network.IsConnected()) { + tvStatus.setText(R.string.no_network); + } + if (left == 0) { + tvRetry.setText(R.string.text_auto_retry_now); + } + else { + tvRetry.setText(String.format(getString(R.string.text_auto_retry_second), left)); + } } @Override public void onFinish() { - if (network.IsConnected()) - client.GetChannels(); - else - RetryGetChannels(); + if (network.IsConnected()) { + volley.add(request); + } + else { + ShowErrorMessage(getString(R.string.no_network), true); + } } }).start(5); } - private void ShowLayoutMessage(int visibility, boolean isMessage) { - layoutStatus.setVisibility(visibility); - if (!isMessage) { - layoutSpin.setVisibility(View.VISIBLE); - layoutText.setVisibility(View.GONE); - } else { - layoutSpin.setVisibility(View.GONE); - layoutText.setVisibility(View.VISIBLE); - } - } - @Override public void onBackPressed() { //super.onBackPressed(); diff --git a/app/src/main/java/net/harimurti/tv/PlayerActivity.java b/app/src/main/java/net/harimurti/tv/PlayerActivity.java index c857047..6f6da74 100644 --- a/app/src/main/java/net/harimurti/tv/PlayerActivity.java +++ b/app/src/main/java/net/harimurti/tv/PlayerActivity.java @@ -2,7 +2,6 @@ import androidx.appcompat.app.AppCompatActivity; -import android.annotation.SuppressLint; import android.net.Uri; import android.os.Bundle; import android.view.View; @@ -32,7 +31,6 @@ public class PlayerActivity extends AppCompatActivity { private View layoutStatus, layoutSpin, layoutText; private TextView tvStatus, tvRetry; - @SuppressLint("all") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -81,7 +79,7 @@ public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { if (playbackState == Player.STATE_IDLE || playbackState == Player.STATE_ENDED) { ShowLayoutMessage(View.VISIBLE, true); tvStatus.setText(R.string.source_offline); - tvRetry.setText(R.string.retry); + tvRetry.setText(R.string.text_auto_retry); RetryPlaying(); } } @@ -91,7 +89,7 @@ public void onPlayerError(ExoPlaybackException error) { ShowLayoutMessage(View.VISIBLE, true); if (error.type == ExoPlaybackException.TYPE_SOURCE) { tvStatus.setText(R.string.source_offline); - tvRetry.setText(R.string.retry); + tvRetry.setText(R.string.text_auto_retry); tvRetry.setVisibility(View.VISIBLE); RetryPlaying(); } else { @@ -128,17 +126,24 @@ private void RetryPlaying() { new AsyncSleep(this).task(new AsyncSleep.Task() { @Override public void onCountDown(int left) { - if (!network.IsConnected()) - tvStatus.setText(getString(R.string.no_network)); - - tvRetry.setText(String.format(getString(R.string.retry_time), left)); + if (!network.IsConnected()) { + tvStatus.setText(R.string.no_network); + } + if (left == 0) { + tvRetry.setText(R.string.text_auto_retry_now); + } + else { + tvRetry.setText(String.format(getString(R.string.text_auto_retry_second), left)); + } } @Override public void onFinish() { - if (network.IsConnected()) + if (network.IsConnected()) { player.prepare(mediaSource); - else + } + else { RetryPlaying(); + } } }).start(5); } diff --git a/app/src/main/java/net/harimurti/tv/adapter/ViewPagerAdapter.java b/app/src/main/java/net/harimurti/tv/adapter/ViewPagerAdapter.java index 7862957..988f1b8 100644 --- a/app/src/main/java/net/harimurti/tv/adapter/ViewPagerAdapter.java +++ b/app/src/main/java/net/harimurti/tv/adapter/ViewPagerAdapter.java @@ -11,10 +11,9 @@ import java.util.ArrayList; -@SuppressWarnings("all") public class ViewPagerAdapter extends FragmentStateAdapter { - private static ArrayList categories; - private static ArrayList channels; + private ArrayList categories; + private ArrayList channels; public ViewPagerAdapter(FragmentActivity activity, Playlist playlist) { super(activity); @@ -22,6 +21,7 @@ public ViewPagerAdapter(FragmentActivity activity, Playlist playlist) { this.channels = playlist.channels; } + @SuppressWarnings("all") @Override public Fragment createFragment(int position) { ArrayList contents = new ArrayList<>(); diff --git a/app/src/main/java/net/harimurti/tv/extra/AsyncSleep.java b/app/src/main/java/net/harimurti/tv/extra/AsyncSleep.java index 4dc99da..f716199 100644 --- a/app/src/main/java/net/harimurti/tv/extra/AsyncSleep.java +++ b/app/src/main/java/net/harimurti/tv/extra/AsyncSleep.java @@ -3,7 +3,6 @@ import android.content.Context; import android.os.Handler; -@SuppressWarnings("all") public class AsyncSleep { private Task task = null; private Context context; @@ -28,10 +27,12 @@ public void start(int second) { new Handler().postDelayed(new Runnable() { @Override public void run() { - if (left == 0) - runOnUiThread(()->task.onFinish()); - else - runOnUiThread(()->task.onCountDown(left)); + runOnUiThread(()-> { + task.onCountDown(left); + if (left == 0) { + task.onFinish(); + } + }); } }, i * 1000); } diff --git a/app/src/main/java/net/harimurti/tv/extra/RestClient.java b/app/src/main/java/net/harimurti/tv/extra/RestClient.java deleted file mode 100644 index 0c2a38d..0000000 --- a/app/src/main/java/net/harimurti/tv/extra/RestClient.java +++ /dev/null @@ -1,73 +0,0 @@ -package net.harimurti.tv.extra; - -import android.content.Context; -import android.util.Log; - -import com.google.gson.Gson; - -import net.harimurti.tv.R; -import net.harimurti.tv.data.Playlist; - -import java.io.IOException; - -import okhttp3.Call; -import okhttp3.Callback; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; - -public class RestClient { - private final String TAG = "RestClient"; - private final OkHttpClient client = new OkHttpClient(); - - private Context context; - private OnClientResult onClientResult; - - public interface OnClientResult { - default void onFailure(String status){} - default void onProgress(boolean status){} - default void onSuccess(Playlist playlist){} - } - - public RestClient setOnClientResult(OnClientResult onClientResult) { - this.onClientResult = onClientResult; - return this; - } - - public RestClient(Context context){ - this.context = context; - } - - @SuppressWarnings("all") - public void GetChannels() { - onClientResult.onProgress(true); - Request request = new Request.Builder() - .url(context.getString(R.string.api_channel)) - .build(); - client.newCall(request).enqueue(new Callback() { - @Override - public void onFailure(Call call, IOException e) { - Log.e(TAG, e.getMessage()); - onClientResult.onFailure(e.getMessage()); - onClientResult.onProgress(false); - } - - @Override - public void onResponse(Call call, Response response) { - try { - // convert json to channel - Playlist playlist = new Gson().fromJson(response.body().string(), Playlist.class); - - onClientResult.onSuccess(playlist); - } - catch (Exception e) { - Log.e(TAG, e.getMessage()); - onClientResult.onFailure(e.getMessage()); - } - finally { - onClientResult.onProgress(false); - } - } - }); - } -} diff --git a/app/src/main/java/net/harimurti/tv/extra/TLSSocketFactory.java b/app/src/main/java/net/harimurti/tv/extra/TLSSocketFactory.java new file mode 100644 index 0000000..f675105 --- /dev/null +++ b/app/src/main/java/net/harimurti/tv/extra/TLSSocketFactory.java @@ -0,0 +1,70 @@ +package net.harimurti.tv.extra; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; + +public class TLSSocketFactory extends SSLSocketFactory { + + private SSLSocketFactory internalSSLSocketFactory; + + public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException { + SSLContext context = SSLContext.getInstance("TLS"); + context.init(null, null, null); + internalSSLSocketFactory = context.getSocketFactory(); + } + + @Override + public String[] getDefaultCipherSuites() { + return internalSSLSocketFactory.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return internalSSLSocketFactory.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket() throws IOException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket()); + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose)); + } + + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port)); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort)); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port)); + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort)); + } + + private Socket enableTLSOnSocket(Socket socket) { + if(socket instanceof SSLSocket) { + ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"}); + } + return socket; + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 528799d..e64e586 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -27,15 +27,15 @@ android:id="@+id/layout_status" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/color_dark_transparent" - android:visibility="gone"> + android:background="@color/color_dark_transparent"> + android:orientation="vertical" + android:visibility="gone"> diff --git a/app/src/main/res/layout/activity_player.xml b/app/src/main/res/layout/activity_player.xml index 7a25bc1..b5dd877 100644 --- a/app/src/main/res/layout/activity_player.xml +++ b/app/src/main/res/layout/activity_player.xml @@ -24,7 +24,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true" - android:orientation="vertical"> + android:orientation="vertical" + android:visibility="gone"> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1f81600..7161db6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3,9 +3,10 @@ Ada sesuatu yang salah!!! Siaran tidak dapat diakses!!! - akan dicoba ulang otomatis dalam 5 - akan dicoba ulang otomatis dalam %d + akan dicoba ulang otomatis dalam 5 + akan dicoba ulang otomatis dalam %d + akan dicoba ulang otomatis sekarang Tidak ada koneksi internet!!! - https://github.com/hariimurti/NontonTV/raw/master/json/playlist.json + https://github.com/hariimurti/NontonTV/raw/master/json/playlist.json diff --git a/app/version.prop b/app/version.prop index 47df8fe..86696eb 100644 --- a/app/version.prop +++ b/app/version.prop @@ -1,4 +1,4 @@ -#Wed Apr 08 17:49:28 ICT 2020 +#Thu Apr 09 21:57:50 ICT 2020 VERSION_MINOR=0 -VERSION_CODE=327 +VERSION_CODE=372 VERSION_MAJOR=1