Java tutorial
/* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package com.mb.android; import android.Manifest; import android.app.Activity; import android.app.AlertDialog; import android.app.DownloadManager; import android.content.BroadcastReceiver; import android.content.ClipData; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.media.AudioManager; import android.net.Uri; import android.net.http.SslCertificate; import android.net.http.SslError; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.preference.PreferenceManager; import android.provider.Settings; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.telephony.TelephonyManager; import android.view.KeyEvent; import android.webkit.WebView; import com.mb.android.api.ApiClientBridge; import com.mb.android.iap.IapLogger; import com.mb.android.iap.IapManager; import com.mb.android.io.NativeFileSystem; import com.mb.android.logging.AppLogger; import com.mb.android.media.MediaService; import com.mb.android.media.VideoPlayerActivity; import com.mb.android.media.legacy.KitKatMediaService; import com.mb.android.media.RemotePlayerService; import org.apache.cordova.CordovaActivity; import com.mb.android.preferences.PreferencesProvider; import com.mb.android.webviews.CrosswalkWebView; import com.mb.android.webviews.IWebView; import com.mb.android.webviews.MySystemWebView; import com.mb.android.webviews.MyXWalkWebViewEngine; import com.mb.android.webviews.NativeWebView; import com.nononsenseapps.filepicker.FilePickerActivity; import org.apache.cordova.CordovaWebViewEngine; import org.apache.cordova.engine.SystemWebViewEngine; import org.crosswalk.engine.XWalkCordovaView; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.regex.Pattern; import mediabrowser.apiinteraction.Response; import mediabrowser.apiinteraction.android.GsonJsonSerializer; import mediabrowser.apiinteraction.android.mediabrowser.Constants; import mediabrowser.apiinteraction.android.sync.MediaSyncAdapter; import mediabrowser.apiinteraction.android.sync.OnDemandSync; import mediabrowser.apiinteraction.http.HttpRequest; import mediabrowser.apiinteraction.http.IAsyncHttpClient; import mediabrowser.model.extensions.StringHelper; import mediabrowser.model.logging.ILogger; import mediabrowser.model.registration.AppstoreRegRequest; import mediabrowser.model.serialization.IJsonSerializer; import tv.emby.iap.InAppProduct; import tv.emby.iap.PurchaseActivity; public class MainActivity extends CordovaActivity { private final int PURCHASE_REQUEST = 999; private final int REQUEST_DIRECTORY = 998; private final int REQUEST_DIRECTORY_SAF = 996; public static final int VIDEO_PLAYBACK = 997; private final String embyAdminUrl = "http://mb3admin.com/test/admin/service/"; private static IWebView webView; private IAsyncHttpClient httpClient; private IJsonSerializer jsonSerializer; private IapManager iapManager; private String purchaseEmail; private InAppProduct currentProduct; public static String AppPackageName = "com.mb.android"; private ILogger getLogger() { return AppLogger.getLogger(this); } private int chromeVersion = 47; public static MainActivity Current; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Current = this; /*new Runnable() { @Override public void run() { readLogcatInBackground(); } }.run();*/ /*try { // This is throwing an exception we can't catch and is crashing the app URL.setURLStreamHandlerFactory(new OkUrlFactory(okHttpClient)); } catch (Exception ex){ // Occasionally seeing factory already set error }*/ // Set by <content src="index.html" /> in config.xml loadUrl(launchUrl); /* Prepare the progressBar */ IntentFilter filter = new IntentFilter(); filter.addAction(Constants.ACTION_SHOW_PLAYER); registerReceiver(messageReceiver, filter); if (enableSystemWebView()) { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // This is causing a crash on some devices WebView.setWebContentsDebuggingEnabled(true); } } catch (Exception ex) { // This is causing a crash on some devices getLogger().ErrorException("Error enabling webview debugging", ex); } addJavascriptInterfaces(); } } private boolean enableSystemWebView() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { getLogger().Info("Enabling Crosswalk due to older android version"); return false; } // Use system version if equal or higher. int systemWebViewVersion = GetSystemWebViewChromiumVersion(); if (systemWebViewVersion < chromeVersion) { getLogger().Info("Enabling Crosswalk due to older chromium version"); return false; } chromeVersion = systemWebViewVersion; return true; } private int GetSystemWebViewChromiumVersion() { try { getLogger().Info("Searching for com.google.android.webview"); PackageManager pm = getPackageManager(); PackageInfo pi = pm.getPackageInfo("com.google.android.webview", 0); getLogger().Info("com.google.android.webview version name: " + pi.versionName); getLogger().Info("com.google.android.webview version code: " + pi.versionCode); String parseString = pi.versionName.split(Pattern.quote("."))[0]; getLogger().Info("Parsing %s to determine chromium version", parseString); int version = Integer.parseInt(parseString); getLogger().Info("Chromium version: " + version); return version; } catch (PackageManager.NameNotFoundException e) { getLogger().ErrorException("Android System WebView is not found", e); return 0; } catch (Exception e) { getLogger().ErrorException("Android System WebView is not found", e); return 0; } } @Override protected CordovaWebViewEngine makeWebViewEngine() { Context context = getApplicationContext(); CordovaWebViewEngine engine; final ILogger logger = getLogger(); if (enableSystemWebView()) { engine = new SystemWebViewEngine(new MySystemWebView(this, logger), preferences); WebView webkitView = (WebView) engine.getView(); webkitView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true); webView = new NativeWebView(webkitView); } else { engine = new MyXWalkWebViewEngine(this, preferences, this); XWalkCordovaView xView = (XWalkCordovaView) engine.getView(); webView = new CrosswalkWebView(xView); } jsonSerializer = new GsonJsonSerializer(); iapManager = new IapManager(context, webView, logger); ApiClientBridge apiClientBridge = new ApiClientBridge(context, logger, webView, jsonSerializer); httpClient = apiClientBridge.httpClient; return engine; } public void addJavascriptInterfaces() { Context context = getApplicationContext(); final ILogger logger = getLogger(); webView.addJavascriptInterface(iapManager, "NativeIapManager"); webView.addJavascriptInterface(ApiClientBridge.Current, "ApiClientBridge"); webView.addJavascriptInterface(new NativeFileSystem(logger, context), "NativeFileSystem"); webView.addJavascriptInterface(this, "MainActivity"); webView.addJavascriptInterface(this, "AndroidDirectoryChooser"); webView.addJavascriptInterface(this, "AndroidVlcPlayer"); webView.addJavascriptInterface(this, "AndroidSync"); PreferencesProvider preferencesProvider = new PreferencesProvider(context, logger); webView.addJavascriptInterface(preferencesProvider, "AndroidSharedPreferences"); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { if (requestCode == PURCHASE_REQUEST) { if (resultCode == RESULT_OK) { if (currentProduct.getEmbyFeatureCode() != null) { AppstoreRegRequest request = new AppstoreRegRequest(); request.setStore(intent.getStringExtra("store")); request.setApplication(AppPackageName); request.setProduct(currentProduct.getSku()); request.setFeature(currentProduct.getEmbyFeatureCode()); request.setType(currentProduct.getProductType().toString()); if (intent.getStringExtra("storeId") != null) request.setStoreId(intent.getStringExtra("storeId")); request.setStoreToken(intent.getStringExtra("storeToken")); request.setEmail(purchaseEmail); request.setAmt(currentProduct.getPrice()); RespondToWebView(String.format("window.IapManager.onPurchaseComplete(" + jsonSerializer.SerializeToString(request) + ");")); } else { // no emby feature - just report success RespondToWebView(String.format("window.IapManager.onPurchaseComplete(true);")); } } else { RespondToWebView(String.format("window.IapManager.onPurchaseComplete(false);")); } } else if (requestCode == REQUEST_DIRECTORY_SAF && resultCode == Activity.RESULT_OK) { Uri uri = intent.getData(); final int takeFlags = intent.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); // Check for the freshest data. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { getContentResolver().takePersistableUriPermission(uri, takeFlags); } RespondToWebviewWithSelectedPath(uri); } else if (requestCode == REQUEST_DIRECTORY && resultCode == RESULT_OK) { if (intent.getBooleanExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false)) { // For JellyBean and above if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { ClipData clip = intent.getClipData(); if (clip != null) { for (int i = 0; i < clip.getItemCount(); i++) { Uri uri = clip.getItemAt(i).getUri(); RespondToWebviewWithSelectedPath(uri); } } // For Ice Cream Sandwich } else { ArrayList<String> paths = intent.getStringArrayListExtra(FilePickerActivity.EXTRA_PATHS); if (paths != null) { for (String path : paths) { Uri uri = Uri.parse(path); RespondToWebviewWithSelectedPath(uri); } } } } else { Uri uri = intent.getData(); // Do something with the URI if (uri != null) { RespondToWebviewWithSelectedPath(uri); } } } else if (requestCode == VIDEO_PLAYBACK) { /*boolean completed = resultCode == RESULT_OK; boolean error = resultCode == RESULT_OK ? false : (intent == null ? true : intent.getBooleanExtra("error", false)); long positionMs = intent == null || completed ? 0 : intent.getLongExtra("position", 0); String currentSrc = intent == null ? null : intent.getStringExtra(VideoPlayerActivity.PLAY_EXTRA_ITEM_LOCATION); if (currentSrc == null) { currentSrc = ""; } RespondToWebView(String.format("VideoRenderer.Current.onActivityClosed(%s, %s, %s, '%s');", !completed, error, positionMs, currentSrc));*/ } } private void RespondToWebviewWithSelectedPath(Uri uri) { String path = uri.toString(); String srch = "file://"; if (StringHelper.IndexOfIgnoreCase(path, srch) == 0) { path = path.substring(srch.length()); } RespondToWebView(String.format("window.NativeDirectoryChooser.onChosen('%s');", path)); } @android.webkit.JavascriptInterface @org.xwalk.core.JavascriptInterface public int getChromeVersion() { return chromeVersion; } @android.webkit.JavascriptInterface @org.xwalk.core.JavascriptInterface public void purchasePremiereMonthly(final String email) { if (iapManager.isStoreAvailable()) { beginPurchase(iapManager.getPremiereMonthly(), email); } else { getLogger().Error("Cannot proceed with purchasePremiereMonthly because store is not available"); } } @android.webkit.JavascriptInterface @org.xwalk.core.JavascriptInterface public void purchasePremiereWeekly(final String email) { if (iapManager.isStoreAvailable()) { beginPurchase(iapManager.getPremiereWeekly(), email); } else { getLogger().Error("Cannot proceed with purchasePremiereWeekly because store is not available"); } } @android.webkit.JavascriptInterface @org.xwalk.core.JavascriptInterface public void purchaseUnlock() { if (iapManager.isStoreAvailable()) { beginPurchase(iapManager.getUnlockProduct(), null); } else { getLogger().Error("Cannot proceed with purchaseUnlock because store is not available"); } } public void beginPurchase(final InAppProduct product, final String purchaseEmail) { this.purchaseEmail = purchaseEmail; if (product.requiresEmail() && (purchaseEmail == null || purchaseEmail.length() == 0)) { //Todo Obtain the email address for purchase - then re-call this method getLogger().Error("Aborting beginPurchase because purchaseEmail is required."); return; } if (product.getEmbyFeatureCode() != null) { //Test connectivity to our back-end before purchase because we need this to complete it getLogger().Debug("Testing back-end connectivity."); HttpRequest request = new HttpRequest(); request.setUrl(embyAdminUrl + "appstore/check"); httpClient.Send(request, new Response<String>() { @Override public void onResponse(String response) { getLogger().Debug("Back-end connectivity test succeeded"); //ok, continue with purchase purchaseInternal(product); } @Override public void onError(Exception exception) { getLogger().Error("Back-end connectivity test failed."); //Unable to connect - display appropriate message } }); } else { //Just initiate the purchase purchaseInternal(product); } } private void purchaseInternal(InAppProduct product) { try { getLogger().Debug("purchaseInternal sku: %s", product.getSku()); currentProduct = product; PurchaseActivity.Logger = new IapLogger(getLogger()); Intent purchaseIntent = new Intent(this, PurchaseActivity.class); purchaseIntent.putExtra("googleKey", IapManager.GOOGLE_KEY); purchaseIntent.putExtra("sku", product.getSku()); startActivityForResult(purchaseIntent, PURCHASE_REQUEST); } catch (Exception ex) { getLogger().ErrorException("Error launching activity", ex); RespondToWebView(String.format("window.IapManager.onPurchaseComplete(false);")); } } public static void RespondToWebView(final String js) { //logger.Info("Sending url to webView: %s", js); if (webView != null) { webView.sendJavaScript(js); } } @android.webkit.JavascriptInterface @org.xwalk.core.JavascriptInterface public void hideMediaSession() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Intent intent = new Intent(this, RemotePlayerService.class); intent.setAction(Constants.ACTION_REPORT); intent.putExtra("playerAction", "playbackstop"); startService(intent); } } @android.webkit.JavascriptInterface @org.xwalk.core.JavascriptInterface public void updateMediaSession(String action, boolean isLocalPlayer, String itemId, String title, String artist, String album, int duration, int position, String imageUrl, boolean canSeek, boolean isPaused) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { getLogger().Info("updateMediaSession isPaused: %s", isPaused); Intent intent = new Intent(this, RemotePlayerService.class); intent.setAction(Constants.ACTION_REPORT); intent.putExtra("playerAction", action); intent.putExtra("title", title); intent.putExtra("artist", artist); intent.putExtra("album", album); intent.putExtra("duration", duration); intent.putExtra("position", position); intent.putExtra("imageUrl", imageUrl); intent.putExtra("canSeek", canSeek); intent.putExtra("isPaused", isPaused); intent.putExtra("itemId", itemId); intent.putExtra("isLocalPlayer", isLocalPlayer); startService(intent); } } private void bluetoothNotifyChange(String action, String title, String artist, String album, long duration, long position, String imageUrl, boolean canSeek, boolean isPaused) { String intentName = action.equalsIgnoreCase("playbackstart") ? "com.android.music.metachanged" : "com.android.music.playstatechanged"; Intent i = new Intent(intentName); i.putExtra("id", 1L); i.putExtra("artist", artist); i.putExtra("album", album); i.putExtra("track", title); i.putExtra("playing", !isPaused); i.putExtra("duration", duration); i.putExtra("position", position); //i.putExtra("ListSize", getQueue()); sendBroadcast(i); } private final int ExternalStoragePermissionRequestCode = 3; private final int AuthorizeStoragePermissionRequestCode = 4; @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case ExternalStoragePermissionRequestCode: { // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // permission was granted, yay! Do the // related task you need to do. chooseDirectory(); } else { // permission denied, boo! Disable the // functionality that depends on this permission. } return; } } } @android.webkit.JavascriptInterface @org.xwalk.core.JavascriptInterface public boolean authorizeStorage() { getLogger().Info("begin authorizeStorage"); return authorizeStorage(AuthorizeStoragePermissionRequestCode); } private boolean authorizeStorage(final int requestCode) { final Activity activity = this; getLogger().Info("authorizeStorage with requestCode %s", requestCode); if (ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { // Should we show an explanation? getLogger().Info("Permission for WRITE_EXTERNAL_STORAGE is not granted"); if (ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show an expanation to the user *asynchronously* -- don't block // this thread waiting for the user's response! After the user // sees the explanation, try again to request the permission. getLogger().Info("calling ActivityCompat.requestPermissions for WRITE_EXTERNAL_STORAGE"); ActivityCompat.requestPermissions(activity, new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, requestCode); } else { // No explanation needed, we can request the permission. getLogger().Info("calling ActivityCompat.requestPermissions for WRITE_EXTERNAL_STORAGE"); ActivityCompat.requestPermissions(activity, new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, requestCode); // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an // app-defined int constant. The callback method gets the // result of the request. } getLogger().Info("authorizeStorage returning false"); return false; } getLogger().Info("Permission for WRITE_EXTERNAL_STORAGE is granted"); getLogger().Info("authorizeStorage returning true"); return true; } @android.webkit.JavascriptInterface @org.xwalk.core.JavascriptInterface public void chooseDirectory() { getLogger().Info("begin chooseDirectory"); if (!authorizeStorage(ExternalStoragePermissionRequestCode)) { return; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); startActivityForResult(intent, REQUEST_DIRECTORY_SAF); } else { getLogger().Info("creating intent for FilePickerActivity"); Intent intent = new Intent(this, FilePickerActivity.class); // This works if you defined the intent filter // Intent i = new Intent(Intent.ACTION_GET_CONTENT); // Set these depending on your use case. These are the defaults. intent.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false); intent.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, true); intent.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR); // Configure initial directory by specifying a String. // You could specify a String like "/storage/emulated/0/", but that can // dangerous. Always use Android's API calls to get paths to the SD-card or // internal memory. intent.putExtra(FilePickerActivity.EXTRA_START_PATH, Environment.getExternalStorageDirectory().getPath()); getLogger().Info("startActivityForResult for FilePickerActivity"); startActivityForResult(intent, REQUEST_DIRECTORY); } } @android.webkit.JavascriptInterface @org.xwalk.core.JavascriptInterface public void playAudioVlc(String path, String itemJson, String mediaSourceJson, String posterUrl) { Intent intent = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { intent = new Intent(this, MediaService.class); } else { intent = new Intent(this, KitKatMediaService.class); } intent.setAction(Constants.ACTION_PLAY); intent.putExtra("path", path); intent.putExtra("item", itemJson); intent.putExtra("mediaSource", mediaSourceJson); intent.putExtra("posterUrl", posterUrl); setVolumeControlStream(AudioManager.STREAM_MUSIC); startService(intent); } @android.webkit.JavascriptInterface @org.xwalk.core.JavascriptInterface public void playVideoVlc(String path, long startPositionMs, String itemName, String itemJson, String mediaSourceJson, String playbackStartInfoJson, String serverId, String serverUrl, String appName, String appVersion, String deviceId, String deviceName, String userId, String accessToken, String deviceProfileJson, String videoQualityOptionsJson, long timeLimitMs) { getLogger().Debug("Video path: %s", path); Intent intent = new Intent(this, VideoPlayerActivity.class); intent.setAction(VideoPlayerActivity.PLAY_FROM_VIDEOGRID); intent.putExtra(VideoPlayerActivity.PLAY_EXTRA_ITEM_LOCATION, path); intent.putExtra(VideoPlayerActivity.PLAY_EXTRA_ITEM_TITLE, itemName); //intent.putExtra(VideoPlayerActivity.PLAY_EXTRA_OPENED_POSITION, 0); //intent.putExtra("item", itemJson); intent.putExtra("mediaSourceJson", mediaSourceJson); intent.putExtra("playbackStartInfoJson", playbackStartInfoJson); intent.putExtra("serverId", serverId); intent.putExtra("serverUrl", serverUrl); intent.putExtra("appName", appName); intent.putExtra("appVersion", appVersion); intent.putExtra("deviceId", deviceId); intent.putExtra("deviceName", deviceName); intent.putExtra("userId", userId); intent.putExtra("accessToken", accessToken); intent.putExtra("deviceProfileJson", deviceProfileJson); intent.putExtra("videoQualityOptionsJson", videoQualityOptionsJson); if (startPositionMs > 0) { intent.putExtra("position", startPositionMs); } if (timeLimitMs > 0) { intent.putExtra("timeLimitMs", timeLimitMs); } startActivityForResult(intent, VIDEO_PLAYBACK); } @android.webkit.JavascriptInterface @org.xwalk.core.JavascriptInterface public void destroyVlc() { Intent intent = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { intent = new Intent(this, MediaService.class); } else { intent = new Intent(this, KitKatMediaService.class); } intent.setAction(Constants.ACTION_STOP); startService(intent); } @Override public void onResume() { super.onResume(); } @Override public void onStop() { super.onStop(); } @android.webkit.JavascriptInterface @org.xwalk.core.JavascriptInterface public void sendVlcCommand(String name, String arg1) { getLogger().Debug("Vlc received command: %s", name); Intent intent = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { intent = new Intent(this, MediaService.class); } else { intent = new Intent(this, KitKatMediaService.class); } try { if (name.equalsIgnoreCase("pause")) { intent.setAction(Constants.ACTION_PAUSE); startService(intent); } else if (name.equalsIgnoreCase("unpause")) { intent.setAction(Constants.ACTION_UNPAUSE); startService(intent); } else if (name.equalsIgnoreCase("stop")) { intent.setAction(Constants.ACTION_STOP); boolean stopService = StringHelper.EqualsIgnoreCase(arg1, "true"); intent.putExtra("stopService", stopService); startService(intent); } else if (name.equalsIgnoreCase("setvolume")) { // incoming value is 0-100 float val = Float.parseFloat(arg1); val = Math.min(val, 100); val = Math.max(val, 0); //mLibVLC.setVolume(Math.round(val)); } else if (name.equalsIgnoreCase("setposition")) { // incoming value is ms intent.setAction(Constants.ACTION_SEEK); getLogger().Debug("Sending seek command to Vlc Service. Position: %s", arg1); try { float newPosition = Float.parseFloat(arg1); long roundedPosition = Math.round(newPosition); intent.putExtra("position", roundedPosition); startService(intent); } catch (NumberFormatException ex) { getLogger().ErrorException("Error parsing seek value", ex); } } } catch (Exception ex) { getLogger().ErrorException("Error sending command %s to Vlc", ex, name); } } private final BroadcastReceiver messageReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equalsIgnoreCase(Constants.ACTION_SHOW_PLAYER)) { // showAudioPlayer(); } } }; @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_MENU) { RespondToWebView("LibraryMenu.onHardwareMenuButtonClick();"); return true; } else { return super.onKeyUp(keyCode, event); } } @android.webkit.JavascriptInterface @org.xwalk.core.JavascriptInterface public String getSyncStatus() { return MediaSyncAdapter.isSyncActive() ? "Active" : MediaSyncAdapter.isSyncPending() ? "Pending" : "Idle"; } @android.webkit.JavascriptInterface @org.xwalk.core.JavascriptInterface public void startSync() { new OnDemandSync(getApplicationContext()).Run(); } @Override public void onDestroy() { super.onDestroy(); try { unregisterReceiver(messageReceiver); } catch (IllegalArgumentException e) { } } static final int BUFFER_SIZE = 2 * 4096; volatile boolean logcatReaderRunning = true; protected void readLogcatInBackground() { logcatReaderRunning = true; Process process = null; try { process = Runtime.getRuntime().exec("logcat"); } catch (IOException e) { logcatReaderRunning = false; return; } BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(process.getInputStream()), BUFFER_SIZE); } catch (IllegalArgumentException e) { logcatReaderRunning = false; } try { while (logcatReaderRunning) { getLogger().Debug(reader.readLine()); } } catch (IOException e) { logcatReaderRunning = false; } } @android.webkit.JavascriptInterface @org.xwalk.core.JavascriptInterface public String getLegacyDeviceId() { Context context = getApplicationContext(); TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); String uuid; String androidID = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); String deviceID = tm.getDeviceId(); String simID = tm.getSimSerialNumber(); if ("9774d56d682e549c".equals(androidID) || androidID == null) { androidID = ""; } if (deviceID == null) { deviceID = ""; } if (simID == null) { simID = ""; } uuid = androidID + deviceID + simID; uuid = String.format("%32s", uuid).replace(' ', '0'); uuid = uuid.substring(0, 32); uuid = uuid.replaceAll("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5"); return uuid; } @android.webkit.JavascriptInterface @org.xwalk.core.JavascriptInterface public boolean supportsPlayStore() { // This determines how Chromecast will be supported // If play store services are available, we use the Google Cast SDK, which is the preferred method // If not, we use the LG Connect SDK return BuildConfig.FLAVOR.toLowerCase().indexOf("google") != -1; } @android.webkit.JavascriptInterface @org.xwalk.core.JavascriptInterface public String getAndroidDeviceId() { return Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID); } @android.webkit.JavascriptInterface @org.xwalk.core.JavascriptInterface public void sendEmail(String to, String subject, String body) { Intent draft = getDraftWithProperties(to, subject, body); String header = "Open with"; final Intent chooser = Intent.createChooser(draft, header); startActivityForResult(chooser, 0); } public Intent getDraftWithProperties(String to, String subject, String body) { Intent mail = new Intent(Intent.ACTION_SEND_MULTIPLE); setSubject(subject, mail); setBody(body, false, mail); setRecipients(to, mail); mail.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); return mail; } /** * Setter for the subject. * * @param subject * The subject of the email. * @param draft * The intent to send. */ private void setSubject(String subject, Intent draft) { draft.putExtra(Intent.EXTRA_SUBJECT, subject); } /** * Setter for the body. * * @param body * The body of the email. * @param isHTML * Indicates the encoding (HTML or plain text). * @param draft * The intent to send. */ private void setBody(String body, Boolean isHTML, Intent draft) { if (isHTML) { /*draft.putExtra(Intent.EXTRA_TEXT, Html.fromHtml(body)); draft.setType("text/html"); if (Build.VERSION.SDK_INT > 15) { draft.putExtra(Intent.EXTRA_HTML_TEXT, body); }*/ } else { draft.putExtra(Intent.EXTRA_TEXT, body); draft.setType("text/plain"); } } private void setRecipients(String to, Intent draft) { String[] receivers = new String[1]; receivers[0] = to; draft.putExtra(Intent.EXTRA_EMAIL, receivers); } @android.webkit.JavascriptInterface @org.xwalk.core.JavascriptInterface public void downloadFile(String url, String path) { getLogger().Info("Downloading file %s", url); String filename = "download"; if (path != null && path.length() > 0) { filename = new File(path).getName(); // This doesn't appear to handle windows paths int index = filename.lastIndexOf('\\'); if (index != -1) { filename = filename.substring(index + 1); } } DownloadManager.Request r = new DownloadManager.Request(android.net.Uri.parse(url)); // This put the download in the same Download dir the browser uses r.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename); // When downloading music and videos they will be listed in the player // (Seems to be available since Honeycomb only) r.allowScanningByMediaScanner(); // Notify user when download is completed // (Seems to be available since Honeycomb only) r.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); // Start download DownloadManager dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); dm.enqueue(r); } public void handleSslError(SslError error, final Response<Boolean> response) { final Context context = this; SslCertificate cert = error.getCertificate(); String issuedTo = cert.getIssuedTo().getDName(); String issuedBy = cert.getIssuedBy().getDName(); String issuedOn = cert.getValidNotBeforeDate().toString(); final String srch = error.getUrl() + "--" + issuedTo + "--" + issuedBy + "--" + issuedOn; final String results = getSharedPreferences(this).getString("acurls1", ""); if (StringHelper.IndexOfIgnoreCase(results, srch) != -1) { response.onResponse(true); return; } final AlertDialog.Builder builder = new AlertDialog.Builder(this); String message = getResources().getString(R.string.notification_error_ssl_cert_invalid) .replace("{0}", issuedTo.replace("localhost", "Emby Server")) .replace("{1}", issuedBy.replace("localhost", "Emby Server")).replace("{2}", issuedOn); builder.setMessage(message); builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { SharedPreferences settings = getSharedPreferences(context); SharedPreferences.Editor editor = settings.edit(); editor.putString("acurls1", results + "|" + srch); // Commit the edits! boolean saved = editor.commit(); response.onResponse(true); } }); builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { response.onResponse(false); } }); final AlertDialog dialog = builder.create(); dialog.show(); } private static SharedPreferences getSharedPreferences(Context context) { return PreferenceManager.getDefaultSharedPreferences(context); } }