diff --git a/.idea/dataSources/791709d9-b4c0-4b2f-87d0-642aa59a89d3/storage_v2/_src_/schema/information_schema.FNRwLQ.meta b/.idea/dataSources/791709d9-b4c0-4b2f-87d0-642aa59a89d3/storage_v2/_src_/schema/information_schema.FNRwLQ.meta new file mode 100644 index 00000000..1ff3db2e --- /dev/null +++ b/.idea/dataSources/791709d9-b4c0-4b2f-87d0-642aa59a89d3/storage_v2/_src_/schema/information_schema.FNRwLQ.meta @@ -0,0 +1,2 @@ +#n:information_schema +! [null, 0, null, null, -2147483648, -2147483648] diff --git a/.idea/dataSources/791709d9-b4c0-4b2f-87d0-642aa59a89d3/storage_v2/_src_/schema/performance_schema.kIw0nw.meta b/.idea/dataSources/791709d9-b4c0-4b2f-87d0-642aa59a89d3/storage_v2/_src_/schema/performance_schema.kIw0nw.meta new file mode 100644 index 00000000..9394db14 --- /dev/null +++ b/.idea/dataSources/791709d9-b4c0-4b2f-87d0-642aa59a89d3/storage_v2/_src_/schema/performance_schema.kIw0nw.meta @@ -0,0 +1,2 @@ +#n:performance_schema +! [null, 0, null, null, -2147483648, -2147483648] diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/AllowList.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/AllowList.java new file mode 100644 index 00000000..4b4bafcc --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/AllowList.java @@ -0,0 +1,170 @@ +/* + 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 org.apache.cordova; + +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.cordova.LOG; + +import android.net.Uri; + +public class AllowList { + private static class URLPattern { + public Pattern scheme; + public Pattern host; + public Integer port; + public Pattern path; + + private String regexFromPattern(String pattern, boolean allowWildcards) { + final String toReplace = "\\.[]{}()^$?+|"; + StringBuilder regex = new StringBuilder(); + for (int i=0; i < pattern.length(); i++) { + char c = pattern.charAt(i); + if (c == '*' && allowWildcards) { + regex.append("."); + } else if (toReplace.indexOf(c) > -1) { + regex.append('\\'); + } + regex.append(c); + } + return regex.toString(); + } + + public URLPattern(String scheme, String host, String port, String path) throws MalformedURLException { + try { + if (scheme == null || "*".equals(scheme)) { + this.scheme = null; + } else { + this.scheme = Pattern.compile(regexFromPattern(scheme, false), Pattern.CASE_INSENSITIVE); + } + if ("*".equals(host)) { + this.host = null; + } else if (host.startsWith("*.")) { + this.host = Pattern.compile("([a-z0-9.-]*\\.)?" + regexFromPattern(host.substring(2), false), Pattern.CASE_INSENSITIVE); + } else { + this.host = Pattern.compile(regexFromPattern(host, false), Pattern.CASE_INSENSITIVE); + } + if (port == null || "*".equals(port)) { + this.port = null; + } else { + this.port = Integer.parseInt(port,10); + } + if (path == null || "/*".equals(path)) { + this.path = null; + } else { + this.path = Pattern.compile(regexFromPattern(path, true)); + } + } catch (NumberFormatException e) { + throw new MalformedURLException("Port must be a number"); + } + } + + public boolean matches(Uri uri) { + try { + return ((scheme == null || scheme.matcher(uri.getScheme()).matches()) && + (host == null || host.matcher(uri.getHost()).matches()) && + (port == null || port.equals(uri.getPort())) && + (path == null || path.matcher(uri.getPath()).matches())); + } catch (Exception e) { + LOG.d(TAG, e.toString()); + return false; + } + } + } + + private ArrayList allowList; + + public static final String TAG = "CordovaAllowList"; + + public AllowList() { + this.allowList = new ArrayList(); + } + + /* Match patterns (from http://developer.chrome.com/extensions/match_patterns.html) + * + * := :// + * := '*' | 'http' | 'https' | 'file' | 'ftp' | 'chrome-extension' + * := '*' | '*.' + + * := '/' + * + * We extend this to explicitly allow a port attached to the host, and we allow + * the scheme to be omitted for backwards compatibility. (Also host is not required + * to begin with a "*" or "*.".) + */ + public void addAllowListEntry(String origin, boolean subdomains) { + if (allowList != null) { + try { + // Unlimited access to network resources + if (origin.compareTo("*") == 0) { + LOG.d(TAG, "Unlimited access to network resources"); + allowList = null; + } + else { // specific access + Pattern parts = Pattern.compile("^((\\*|[A-Za-z-]+):(//)?)?(\\*|((\\*\\.)?[^*/:]+))?(:(\\d+))?(/.*)?"); + Matcher m = parts.matcher(origin); + if (m.matches()) { + String scheme = m.group(2); + String host = m.group(4); + // Special case for two urls which are allowed to have empty hosts + if (("file".equals(scheme) || "content".equals(scheme)) && host == null) host = "*"; + String port = m.group(8); + String path = m.group(9); + if (scheme == null) { + // XXX making it stupid friendly for people who forget to include protocol/SSL + allowList.add(new URLPattern("http", host, port, path)); + allowList.add(new URLPattern("https", host, port, path)); + } else { + allowList.add(new URLPattern(scheme, host, port, path)); + } + } + } + } catch (Exception e) { + LOG.d(TAG, "Failed to add origin %s", origin); + } + } + } + + + /** + * Determine if URL is in approved list of URLs to load. + * + * @param uri + * @return true if wide open or allow listed + */ + public boolean isUrlAllowListed(String uri) { + // If there is no allowList, then it's wide open + if (allowList == null) return true; + + Uri parsedUri = Uri.parse(uri); + // Look for match in allow list + Iterator pit = allowList.iterator(); + while (pit.hasNext()) { + URLPattern p = pit.next(); + if (p.matches(parsedUri)) { + return true; + } + } + return false; + } + +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/AuthenticationToken.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/AuthenticationToken.java new file mode 100644 index 00000000..d3a231a0 --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/AuthenticationToken.java @@ -0,0 +1,69 @@ +/* + 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 org.apache.cordova; + +/** + * The Class AuthenticationToken defines the userName and password to be used for authenticating a web resource + */ +public class AuthenticationToken { + private String userName; + private String password; + + /** + * Gets the user name. + * + * @return the user name + */ + public String getUserName() { + return userName; + } + + /** + * Sets the user name. + * + * @param userName + * the new user name + */ + public void setUserName(String userName) { + this.userName = userName; + } + + /** + * Gets the password. + * + * @return the password + */ + public String getPassword() { + return password; + } + + /** + * Sets the password. + * + * @param password + * the new password + */ + public void setPassword(String password) { + this.password = password; + } + + + + +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/BuildHelper.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/BuildHelper.java new file mode 100644 index 00000000..2899ee2a --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/BuildHelper.java @@ -0,0 +1,74 @@ +/* + 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 org.apache.cordova; + +/* + * This is a utility class that allows us to get the BuildConfig variable, which is required + * for the use of different providers. This is not guaranteed to work, and it's better for this + * to be set in the build step in config.xml + * + */ + +import android.app.Activity; +import android.content.Context; + +import java.lang.reflect.Field; + + +public class BuildHelper { + + + private static String TAG="BuildHelper"; + + /* + * This needs to be implemented if you wish to use the Camera Plugin or other plugins + * that read the Build Configuration. + * + * Thanks to Phil@Medtronic and Graham Borland for finding the answer and posting it to + * StackOverflow. This is annoying as hell! However, this method does not work with + * ProGuard, and you should use the config.xml to define the application_id + * + */ + + public static Object getBuildConfigValue(Context ctx, String key) + { + try + { + String packageName = ctx.getApplicationInfo().packageName; + Class clazz = Class.forName(packageName + ".BuildConfig"); + Field field = clazz.getField(key); + return field.get(null); + } catch (ClassNotFoundException e) { + LOG.d(TAG, "Unable to get the BuildConfig, is this built with ANT?"); + e.printStackTrace(); + } catch (NoSuchFieldException e) { + LOG.d(TAG, key + " is not a valid field. Check your build.gradle"); + } catch (IllegalAccessException e) { + LOG.d(TAG, "Illegal Access Exception: Let's print a stack trace."); + e.printStackTrace(); + } catch (NullPointerException e) { + LOG.d(TAG, "Null Pointer Exception: Let's print a stack trace."); + e.printStackTrace(); + } + + return null; + } + +} \ No newline at end of file diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CallbackContext.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CallbackContext.java new file mode 100644 index 00000000..43363869 --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CallbackContext.java @@ -0,0 +1,142 @@ +/* + 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 org.apache.cordova; + +import org.json.JSONArray; + +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.PluginResult; +import org.json.JSONObject; + +public class CallbackContext { + private static final String LOG_TAG = "CordovaPlugin"; + + private String callbackId; + private CordovaWebView webView; + protected boolean finished; + private int changingThreads; + + public CallbackContext(String callbackId, CordovaWebView webView) { + this.callbackId = callbackId; + this.webView = webView; + } + + public boolean isFinished() { + return finished; + } + + public boolean isChangingThreads() { + return changingThreads > 0; + } + + public String getCallbackId() { + return callbackId; + } + + public void sendPluginResult(PluginResult pluginResult) { + synchronized (this) { + if (finished) { + LOG.w(LOG_TAG, "Attempted to send a second callback for ID: " + callbackId + "\nResult was: " + pluginResult.getMessage()); + return; + } else { + finished = !pluginResult.getKeepCallback(); + } + } + webView.sendPluginResult(pluginResult, callbackId); + } + + /** + * Helper for success callbacks that just returns the Status.OK by default + * + * @param message The message to add to the success result. + */ + public void success(JSONObject message) { + sendPluginResult(new PluginResult(PluginResult.Status.OK, message)); + } + + /** + * Helper for success callbacks that just returns the Status.OK by default + * + * @param message The message to add to the success result. + */ + public void success(String message) { + sendPluginResult(new PluginResult(PluginResult.Status.OK, message)); + } + + /** + * Helper for success callbacks that just returns the Status.OK by default + * + * @param message The message to add to the success result. + */ + public void success(JSONArray message) { + sendPluginResult(new PluginResult(PluginResult.Status.OK, message)); + } + + /** + * Helper for success callbacks that just returns the Status.OK by default + * + * @param message The message to add to the success result. + */ + public void success(byte[] message) { + sendPluginResult(new PluginResult(PluginResult.Status.OK, message)); + } + + /** + * Helper for success callbacks that just returns the Status.OK by default + * + * @param message The message to add to the success result. + */ + public void success(int message) { + sendPluginResult(new PluginResult(PluginResult.Status.OK, message)); + } + + /** + * Helper for success callbacks that just returns the Status.OK by default + */ + public void success() { + sendPluginResult(new PluginResult(PluginResult.Status.OK)); + } + + /** + * Helper for error callbacks that just returns the Status.ERROR by default + * + * @param message The message to add to the error result. + */ + public void error(JSONObject message) { + sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message)); + } + + /** + * Helper for error callbacks that just returns the Status.ERROR by default + * + * @param message The message to add to the error result. + */ + public void error(String message) { + sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message)); + } + + /** + * Helper for error callbacks that just returns the Status.ERROR by default + * + * @param message The message to add to the error result. + */ + public void error(int message) { + sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message)); + } +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CallbackMap.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CallbackMap.java new file mode 100644 index 00000000..050daa01 --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CallbackMap.java @@ -0,0 +1,65 @@ +/* + 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 org.apache.cordova; + +import android.util.Pair; +import android.util.SparseArray; + +/** + * Provides a collection that maps unique request codes to CordovaPlugins and Integers. + * Used to ensure that when plugins make requests for runtime permissions, those requests do not + * collide with requests from other plugins that use the same request code value. + */ +public class CallbackMap { + private int currentCallbackId = 0; + private SparseArray> callbacks; + + public CallbackMap() { + this.callbacks = new SparseArray>(); + } + + /** + * Stores a CordovaPlugin and request code and returns a new unique request code to use + * in a permission request. + * + * @param receiver The plugin that is making the request + * @param requestCode The original request code used by the plugin + * @return A unique request code that can be used to retrieve this callback + * with getAndRemoveCallback() + */ + public synchronized int registerCallback(CordovaPlugin receiver, int requestCode) { + int mappedId = this.currentCallbackId++; + callbacks.put(mappedId, new Pair(receiver, requestCode)); + return mappedId; + } + + /** + * Retrieves and removes a callback stored in the map using the mapped request code + * obtained from registerCallback() + * + * @param mappedId The request code obtained from registerCallback() + * @return The CordovaPlugin and orignal request code that correspond to the + * given mappedCode + */ + public synchronized Pair getAndRemoveCallback(int mappedId) { + Pair callback = callbacks.get(mappedId); + callbacks.remove(mappedId); + return callback; + } +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/Config.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/Config.java new file mode 100644 index 00000000..238cd5e3 --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/Config.java @@ -0,0 +1,71 @@ +/* + 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 org.apache.cordova; + +import java.util.List; + +import android.app.Activity; + +@Deprecated // Use AllowList, CordovaPrefences, etc. directly. +public class Config { + private static final String TAG = "Config"; + + static ConfigXmlParser parser; + + private Config() { + } + + public static void init(Activity action) { + parser = new ConfigXmlParser(); + parser.parse(action); + //TODO: Add feature to bring this back. Some preferences should be overridden by intents, but not all + parser.getPreferences().setPreferencesBundle(action.getIntent().getExtras()); + } + + // Intended to be used for testing only; creates an empty configuration. + public static void init() { + if (parser == null) { + parser = new ConfigXmlParser(); + } + } + + public static String getStartUrl() { + if (parser == null) { + return "file:///android_asset/www/index.html"; + } + return parser.getLaunchUrl(); + } + + public static String getErrorUrl() { + return parser.getPreferences().getString("errorurl", null); + } + + public static List getPluginEntries() { + return parser.getPluginEntries(); + } + + public static CordovaPreferences getPreferences() { + return parser.getPreferences(); + } + + public static boolean isInitialized() { + return parser != null; + } +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/ConfigXmlParser.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/ConfigXmlParser.java new file mode 100644 index 00000000..0b92e96b --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/ConfigXmlParser.java @@ -0,0 +1,209 @@ +/* + 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 org.apache.cordova; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import android.content.Context; + +public class ConfigXmlParser { + private static String TAG = "ConfigXmlParser"; + + private static String SCHEME_HTTP = "http"; + private static String SCHEME_HTTPS = "https"; + private static String DEFAULT_HOSTNAME = "localhost"; + private static final String DEFAULT_CONTENT_SRC = "index.html"; + + private String launchUrl; + private String contentSrc; + private CordovaPreferences prefs = new CordovaPreferences(); + private ArrayList pluginEntries = new ArrayList(20); + + public CordovaPreferences getPreferences() { + return prefs; + } + + public ArrayList getPluginEntries() { + return pluginEntries; + } + + public String getLaunchUrl() { + if (launchUrl == null) { + setStartUrl(contentSrc); + } + + return launchUrl; + } + + public void parse(Context action) { + // First checking the class namespace for config.xml + int id = action.getResources().getIdentifier("config", "xml", action.getClass().getPackage().getName()); + if (id == 0) { + // If we couldn't find config.xml there, we'll look in the namespace from AndroidManifest.xml + id = action.getResources().getIdentifier("config", "xml", action.getPackageName()); + if (id == 0) { + LOG.e(TAG, "res/xml/config.xml is missing!"); + return; + } + } + + pluginEntries.add( + new PluginEntry( + AllowListPlugin.PLUGIN_NAME, + "org.apache.cordova.AllowListPlugin", + true + ) + ); + + pluginEntries.add( + new PluginEntry( + SplashScreenPlugin.PLUGIN_NAME, + "org.apache.cordova.SplashScreenPlugin", + true + ) + ); + + parse(action.getResources().getXml(id)); + } + + boolean insideFeature = false; + String service = "", pluginClass = "", paramType = ""; + boolean onload = false; + + public void parse(XmlPullParser xml) { + int eventType = -1; + + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG) { + handleStartTag(xml); + } + else if (eventType == XmlPullParser.END_TAG) + { + handleEndTag(xml); + } + try { + eventType = xml.next(); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + onPostParse(); + } + + private void onPostParse() { + // After parsing, if contentSrc is still null, it signals + // that tag was completely missing. In this case, + // default it. + // https://github.com/apache/cordova-android/issues/1432 + if (contentSrc == null) { + contentSrc = DEFAULT_CONTENT_SRC; + } + } + + public void handleStartTag(XmlPullParser xml) { + String strNode = xml.getName(); + if (strNode.equals("feature")) { + //Check for supported feature sets aka. plugins (Accelerometer, Geolocation, etc) + //Set the bit for reading params + insideFeature = true; + service = xml.getAttributeValue(null, "name"); + } + else if (insideFeature && strNode.equals("param")) { + paramType = xml.getAttributeValue(null, "name"); + if (paramType.equals("service")) // check if it is using the older service param + service = xml.getAttributeValue(null, "value"); + else if (paramType.equals("package") || paramType.equals("android-package")) + pluginClass = xml.getAttributeValue(null,"value"); + else if (paramType.equals("onload")) + onload = "true".equals(xml.getAttributeValue(null, "value")); + } + else if (strNode.equals("preference")) { + String name = xml.getAttributeValue(null, "name").toLowerCase(Locale.ENGLISH); + String value = xml.getAttributeValue(null, "value"); + prefs.set(name, value); + } + else if (strNode.equals("content")) { + String src = xml.getAttributeValue(null, "src"); + if (src != null) { + contentSrc = src; + } else { + // Default + contentSrc = DEFAULT_CONTENT_SRC; + } + } + } + + public void handleEndTag(XmlPullParser xml) { + String strNode = xml.getName(); + if (strNode.equals("feature")) { + pluginEntries.add(new PluginEntry(service, pluginClass, onload)); + + service = ""; + pluginClass = ""; + insideFeature = false; + onload = false; + } + } + + private String getLaunchUrlPrefix() { + if (prefs.getBoolean("AndroidInsecureFileModeEnabled", false)) { + return "file:///android_asset/www/"; + } else { + String scheme = prefs.getString("scheme", SCHEME_HTTPS).toLowerCase(); + String hostname = prefs.getString("hostname", DEFAULT_HOSTNAME).toLowerCase(); + + if (!scheme.contentEquals(SCHEME_HTTP) && !scheme.contentEquals(SCHEME_HTTPS)) { + LOG.d(TAG, "The provided scheme \"" + scheme + "\" is not valid. " + + "Defaulting to \"" + SCHEME_HTTPS + "\". " + + "(Valid Options=" + SCHEME_HTTP + "," + SCHEME_HTTPS + ")"); + + scheme = SCHEME_HTTPS; + } + + return scheme + "://" + hostname + '/'; + } + } + + private void setStartUrl(String src) { + Pattern schemeRegex = Pattern.compile("^[a-z-]+://"); + Matcher matcher = schemeRegex.matcher(src); + + if (matcher.find()) { + launchUrl = src; + } else { + String launchUrlPrefix = getLaunchUrlPrefix(); + + // remove leading slash, "/", from content src if existing, + if (src.charAt(0) == '/') { + src = src.substring(1); + } + + launchUrl = launchUrlPrefix + src; + } + } +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaActivity.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaActivity.java new file mode 100755 index 00000000..ef6ae5e5 --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaActivity.java @@ -0,0 +1,536 @@ +/* + 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 org.apache.cordova; + +import java.util.ArrayList; +import java.util.Locale; + +import org.json.JSONException; +import org.json.JSONObject; + +import android.app.AlertDialog; +import android.annotation.SuppressLint; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.res.Configuration; +import android.graphics.Color; +import android.media.AudioManager; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.webkit.WebViewClient; +import android.widget.FrameLayout; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.splashscreen.SplashScreen; + +/** + * This class is the main Android activity that represents the Cordova + * application. It should be extended by the user to load the specific + * html file that contains the application. + * + * As an example: + * + *
+ *     package org.apache.cordova.examples;
+ *
+ *     import android.os.Bundle;
+ *     import org.apache.cordova.*;
+ *
+ *     public class Example extends CordovaActivity {
+ *       @Override
+ *       public void onCreate(Bundle savedInstanceState) {
+ *         super.onCreate(savedInstanceState);
+ *         super.init();
+ *         // Load your application
+ *         loadUrl(launchUrl);
+ *       }
+ *     }
+ * 
+ * + * Cordova xml configuration: Cordova uses a configuration file at + * res/xml/config.xml to specify its settings. See "The config.xml File" + * guide in cordova-docs at http://cordova.apache.org/docs for the documentation + * for the configuration. The use of the set*Property() methods is + * deprecated in favor of the config.xml file. + * + */ +public class CordovaActivity extends AppCompatActivity { + public static String TAG = "CordovaActivity"; + + // The webview for our app + protected CordovaWebView appView; + + private static int ACTIVITY_STARTING = 0; + private static int ACTIVITY_RUNNING = 1; + private static int ACTIVITY_EXITING = 2; + + // Keep app running when pause is received. (default = true) + // If true, then the JavaScript and native code continue to run in the background + // when another application (activity) is started. + protected boolean keepRunning = true; + + // Flag to keep immersive mode if set to fullscreen + protected boolean immersiveMode; + + // Read from config.xml: + protected CordovaPreferences preferences; + protected String launchUrl; + protected ArrayList pluginEntries; + protected CordovaInterfaceImpl cordovaInterface; + + private SplashScreen splashScreen; + + /** + * Called when the activity is first created. + */ + @Override + public void onCreate(Bundle savedInstanceState) { + // Handle the splash screen transition. + splashScreen = SplashScreen.installSplashScreen(this); + + // need to activate preferences before super.onCreate to avoid "requestFeature() must be called before adding content" exception + loadConfig(); + + String logLevel = preferences.getString("loglevel", "ERROR"); + LOG.setLogLevel(logLevel); + + LOG.i(TAG, "Apache Cordova native platform version " + CordovaWebView.CORDOVA_VERSION + " is starting"); + LOG.d(TAG, "CordovaActivity.onCreate()"); + + if (!preferences.getBoolean("ShowTitle", false)) { + getWindow().requestFeature(Window.FEATURE_NO_TITLE); + } + + if (preferences.getBoolean("SetFullscreen", false)) { + LOG.d(TAG, "The SetFullscreen configuration is deprecated in favor of Fullscreen, and will be removed in a future version."); + preferences.set("Fullscreen", true); + } + if (preferences.getBoolean("Fullscreen", false)) { + // NOTE: use the FullscreenNotImmersive configuration key to set the activity in a REAL full screen + // (as was the case in previous cordova versions) + if (!preferences.getBoolean("FullscreenNotImmersive", false)) { + immersiveMode = true; + setImmersiveUiVisibility(); + } else { + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + } else { + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + } + + super.onCreate(savedInstanceState); + + cordovaInterface = makeCordovaInterface(); + if (savedInstanceState != null) { + cordovaInterface.restoreInstanceState(savedInstanceState); + } + } + + protected void init() { + appView = makeWebView(); + createViews(); + if (!appView.isInitialized()) { + appView.init(cordovaInterface, pluginEntries, preferences); + } + cordovaInterface.onCordovaInit(appView.getPluginManager()); + + // Setup the splash screen based on preference settings + cordovaInterface.pluginManager.postMessage("setupSplashScreen", splashScreen); + + // Wire the hardware volume controls to control media if desired. + String volumePref = preferences.getString("DefaultVolumeStream", ""); + if ("media".equals(volumePref.toLowerCase(Locale.ENGLISH))) { + setVolumeControlStream(AudioManager.STREAM_MUSIC); + } + } + + @SuppressWarnings("deprecation") + protected void loadConfig() { + ConfigXmlParser parser = new ConfigXmlParser(); + parser.parse(this); + preferences = parser.getPreferences(); + preferences.setPreferencesBundle(getIntent().getExtras()); + launchUrl = parser.getLaunchUrl(); + pluginEntries = parser.getPluginEntries(); + Config.parser = parser; + } + + //Suppressing warnings in AndroidStudio + @SuppressWarnings({"deprecation", "ResourceType"}) + protected void createViews() { + //Why are we setting a constant as the ID? This should be investigated + appView.getView().setId(100); + appView.getView().setLayoutParams(new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + setContentView(appView.getView()); + + if (preferences.contains("BackgroundColor")) { + try { + int backgroundColor = preferences.getInteger("BackgroundColor", Color.BLACK); + // Background of activity: + appView.getView().setBackgroundColor(backgroundColor); + } + catch (NumberFormatException e){ + e.printStackTrace(); + } + } + + appView.getView().requestFocusFromTouch(); + } + + /** + * Construct the default web view object. + *

+ * Override this to customize the webview that is used. + */ + protected CordovaWebView makeWebView() { + return new CordovaWebViewImpl(makeWebViewEngine()); + } + + protected CordovaWebViewEngine makeWebViewEngine() { + return CordovaWebViewImpl.createEngine(this, preferences); + } + + protected CordovaInterfaceImpl makeCordovaInterface() { + return new CordovaInterfaceImpl(this) { + @Override + public Object onMessage(String id, Object data) { + // Plumb this to CordovaActivity.onMessage for backwards compatibility + return CordovaActivity.this.onMessage(id, data); + } + }; + } + + /** + * Load the url into the webview. + */ + public void loadUrl(String url) { + if (appView == null) { + init(); + } + + // If keepRunning + this.keepRunning = preferences.getBoolean("KeepRunning", true); + + appView.loadUrlIntoView(url, true); + } + + /** + * Called when the system is about to start resuming a previous activity. + */ + @Override + protected void onPause() { + super.onPause(); + LOG.d(TAG, "Paused the activity."); + + if (this.appView != null) { + // CB-9382 If there is an activity that started for result and main activity is waiting for callback + // result, we shoudn't stop WebView Javascript timers, as activity for result might be using them + boolean keepRunning = this.keepRunning || this.cordovaInterface.activityResultCallback != null; + this.appView.handlePause(keepRunning); + } + } + + /** + * Called when the activity receives a new intent + */ + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + //Forward to plugins + if (this.appView != null) + this.appView.onNewIntent(intent); + } + + /** + * Called when the activity will start interacting with the user. + */ + @Override + protected void onResume() { + super.onResume(); + LOG.d(TAG, "Resumed the activity."); + + if (this.appView == null) { + return; + } + if (! this.getWindow().getDecorView().hasFocus()) { + // Force window to have focus, so application always + // receive user input. Workaround for some devices (Samsung Galaxy Note 3 at least) + this.getWindow().getDecorView().requestFocus(); + } + + this.appView.handleResume(this.keepRunning); + } + + /** + * Called when the activity is no longer visible to the user. + */ + @Override + protected void onStop() { + super.onStop(); + LOG.d(TAG, "Stopped the activity."); + + if (this.appView == null) { + return; + } + this.appView.handleStop(); + } + + /** + * Called when the activity is becoming visible to the user. + */ + @Override + protected void onStart() { + super.onStart(); + LOG.d(TAG, "Started the activity."); + + if (this.appView == null) { + return; + } + this.appView.handleStart(); + } + + /** + * The final call you receive before your activity is destroyed. + */ + @Override + public void onDestroy() { + LOG.d(TAG, "CordovaActivity.onDestroy()"); + super.onDestroy(); + + if (this.appView != null) { + appView.handleDestroy(); + } + } + + /** + * Called when view focus is changed + */ + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + if (hasFocus && immersiveMode) { + setImmersiveUiVisibility(); + } + } + + @SuppressLint("InlinedApi") + protected void setImmersiveUiVisibility() { + final int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + + getWindow().getDecorView().setSystemUiVisibility(uiOptions); + } + + @SuppressLint("NewApi") + @Override + public void startActivityForResult(Intent intent, int requestCode, Bundle options) { + // Capture requestCode here so that it is captured in the setActivityResultCallback() case. + cordovaInterface.setActivityResultRequestCode(requestCode); + super.startActivityForResult(intent, requestCode, options); + } + + /** + * Called when an activity you launched exits, giving you the requestCode you started it with, + * the resultCode it returned, and any additional data from it. + * + * @param requestCode The request code originally supplied to startActivityForResult(), + * allowing you to identify who this result came from. + * @param resultCode The integer result code returned by the child activity through its setResult(). + * @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras"). + */ + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent intent) { + LOG.d(TAG, "Incoming Result. Request code = " + requestCode); + super.onActivityResult(requestCode, resultCode, intent); + cordovaInterface.onActivityResult(requestCode, resultCode, intent); + } + + /** + * Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable). + * The errorCode parameter corresponds to one of the ERROR_* constants. + * + * @param errorCode The error code corresponding to an ERROR_* value. + * @param description A String describing the error. + * @param failingUrl The url that failed to load. + */ + public void onReceivedError(final int errorCode, final String description, final String failingUrl) { + final CordovaActivity me = this; + + // If errorUrl specified, then load it + final String errorUrl = preferences.getString("errorUrl", null); + if ((errorUrl != null) && (!failingUrl.equals(errorUrl)) && (appView != null)) { + // Load URL on UI thread + me.runOnUiThread(new Runnable() { + public void run() { + me.appView.showWebPage(errorUrl, false, true, null); + } + }); + } + // If not, then display error dialog + else { + final boolean exit = !(errorCode == WebViewClient.ERROR_HOST_LOOKUP); + me.runOnUiThread(new Runnable() { + public void run() { + if (exit) { + me.appView.getView().setVisibility(View.GONE); + me.displayError("Application Error", description + " (" + failingUrl + ")", "OK", exit); + } + } + }); + } + } + + /** + * Display an error dialog and optionally exit application. + */ + public void displayError(final String title, final String message, final String button, final boolean exit) { + final CordovaActivity me = this; + me.runOnUiThread(new Runnable() { + public void run() { + try { + AlertDialog.Builder dlg = new AlertDialog.Builder(me); + dlg.setMessage(message); + dlg.setTitle(title); + dlg.setCancelable(false); + dlg.setPositiveButton(button, + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + if (exit) { + finish(); + } + } + }); + dlg.create(); + dlg.show(); + } catch (Exception e) { + finish(); + } + } + }); + } + + /* + * Hook in Cordova for menu plugins + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + if (appView != null) { + appView.getPluginManager().postMessage("onCreateOptionsMenu", menu); + } + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + if (appView != null) { + appView.getPluginManager().postMessage("onPrepareOptionsMenu", menu); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (appView != null) { + appView.getPluginManager().postMessage("onOptionsItemSelected", item); + } + return true; + } + + /** + * Called when a message is sent to plugin. + * + * @param id The message id + * @param data The message data + * @return Object or null + */ + public Object onMessage(String id, Object data) { + if ("onReceivedError".equals(id)) { + JSONObject d = (JSONObject) data; + try { + this.onReceivedError(d.getInt("errorCode"), d.getString("description"), d.getString("url")); + } catch (JSONException e) { + e.printStackTrace(); + } + } else if ("exit".equals(id)) { + finish(); + } + return null; + } + + protected void onSaveInstanceState(Bundle outState) { + cordovaInterface.onSaveInstanceState(outState); + super.onSaveInstanceState(outState); + } + + /** + * Called by the system when the device configuration changes while your activity is running. + * + * @param newConfig The new device configuration + */ + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + if (this.appView == null) { + return; + } + PluginManager pm = this.appView.getPluginManager(); + if (pm != null) { + pm.onConfigurationChanged(newConfig); + } + } + + /** + * Called by the system when the user grants permissions + * + * @param requestCode + * @param permissions + * @param grantResults + */ + @Override + public void onRequestPermissionsResult(int requestCode, String permissions[], + int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + + try + { + cordovaInterface.onRequestPermissionResult(requestCode, permissions, grantResults); + } + catch (JSONException e) + { + LOG.d(TAG, "JSONException: Parameters fed into the method are not valid"); + e.printStackTrace(); + } + + } +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaArgs.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaArgs.java new file mode 100644 index 00000000..d40d26eb --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaArgs.java @@ -0,0 +1,113 @@ +/* + 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 org.apache.cordova; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.util.Base64; + +public class CordovaArgs { + private JSONArray baseArgs; + + public CordovaArgs(JSONArray args) { + this.baseArgs = args; + } + + + // Pass through the basics to the base args. + public Object get(int index) throws JSONException { + return baseArgs.get(index); + } + + public boolean getBoolean(int index) throws JSONException { + return baseArgs.getBoolean(index); + } + + public double getDouble(int index) throws JSONException { + return baseArgs.getDouble(index); + } + + public int getInt(int index) throws JSONException { + return baseArgs.getInt(index); + } + + public JSONArray getJSONArray(int index) throws JSONException { + return baseArgs.getJSONArray(index); + } + + public JSONObject getJSONObject(int index) throws JSONException { + return baseArgs.getJSONObject(index); + } + + public long getLong(int index) throws JSONException { + return baseArgs.getLong(index); + } + + public String getString(int index) throws JSONException { + return baseArgs.getString(index); + } + + + public Object opt(int index) { + return baseArgs.opt(index); + } + + public boolean optBoolean(int index) { + return baseArgs.optBoolean(index); + } + + public double optDouble(int index) { + return baseArgs.optDouble(index); + } + + public int optInt(int index) { + return baseArgs.optInt(index); + } + + public JSONArray optJSONArray(int index) { + return baseArgs.optJSONArray(index); + } + + public JSONObject optJSONObject(int index) { + return baseArgs.optJSONObject(index); + } + + public long optLong(int index) { + return baseArgs.optLong(index); + } + + public String optString(int index) { + return baseArgs.optString(index); + } + + public boolean isNull(int index) { + return baseArgs.isNull(index); + } + + + // The interesting custom helpers. + public byte[] getArrayBuffer(int index) throws JSONException { + String encoded = baseArgs.getString(index); + return Base64.decode(encoded, Base64.DEFAULT); + } +} + + diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaBridge.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaBridge.java new file mode 100644 index 00000000..a8e83692 --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaBridge.java @@ -0,0 +1,186 @@ +/* + 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 org.apache.cordova; + +import android.annotation.SuppressLint; + +import java.security.SecureRandom; + +import org.json.JSONArray; +import org.json.JSONException; + +/** + * Contains APIs that the JS can call. All functions in here should also have + * an equivalent entry in CordovaChromeClient.java, and be added to + * cordova-js/lib/android/plugin/android/promptbasednativeapi.js + */ +public class CordovaBridge { + private static final String LOG_TAG = "CordovaBridge"; + private PluginManager pluginManager; + private NativeToJsMessageQueue jsMessageQueue; + private volatile int expectedBridgeSecret = -1; // written by UI thread, read by JS thread. + + public CordovaBridge(PluginManager pluginManager, NativeToJsMessageQueue jsMessageQueue) { + this.pluginManager = pluginManager; + this.jsMessageQueue = jsMessageQueue; + } + + public String jsExec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException { + if (!verifySecret("exec()", bridgeSecret)) { + return null; + } + // If the arguments weren't received, send a message back to JS. It will switch bridge modes and try again. See CB-2666. + // We send a message meant specifically for this case. It starts with "@" so no other message can be encoded into the same string. + if (arguments == null) { + return "@Null arguments."; + } + + jsMessageQueue.setPaused(true); + try { + // Tell the resourceApi what thread the JS is running on. + CordovaResourceApi.jsThread = Thread.currentThread(); + + pluginManager.exec(service, action, callbackId, arguments); + String ret = null; + if (!NativeToJsMessageQueue.DISABLE_EXEC_CHAINING) { + ret = jsMessageQueue.popAndEncode(false); + } + return ret; + } catch (Throwable e) { + e.printStackTrace(); + return ""; + } finally { + jsMessageQueue.setPaused(false); + } + } + + public void jsSetNativeToJsBridgeMode(int bridgeSecret, int value) throws IllegalAccessException { + if (!verifySecret("setNativeToJsBridgeMode()", bridgeSecret)) { + return; + } + jsMessageQueue.setBridgeMode(value); + } + + public String jsRetrieveJsMessages(int bridgeSecret, boolean fromOnlineEvent) throws IllegalAccessException { + if (!verifySecret("retrieveJsMessages()", bridgeSecret)) { + return null; + } + return jsMessageQueue.popAndEncode(fromOnlineEvent); + } + + private boolean verifySecret(String action, int bridgeSecret) throws IllegalAccessException { + if (!jsMessageQueue.isBridgeEnabled()) { + if (bridgeSecret == -1) { + LOG.d(LOG_TAG, action + " call made before bridge was enabled."); + } else { + LOG.d(LOG_TAG, "Ignoring " + action + " from previous page load."); + } + return false; + } + // Bridge secret wrong and bridge not due to it being from the previous page. + if (expectedBridgeSecret < 0 || bridgeSecret != expectedBridgeSecret) { + LOG.e(LOG_TAG, "Bridge access attempt with wrong secret token, possibly from malicious code. Disabling exec() bridge!"); + clearBridgeSecret(); + throw new IllegalAccessException(); + } + return true; + } + + /** Called on page transitions */ + void clearBridgeSecret() { + expectedBridgeSecret = -1; + } + + public boolean isSecretEstablished() { + return expectedBridgeSecret != -1; + } + + /** Called by cordova.js to initialize the bridge. */ + //On old Androids SecureRandom isn't really secure, this is the least of your problems if + //you're running Android 4.3 and below in 2017 + int generateBridgeSecret() { + SecureRandom randGen = new SecureRandom(); + expectedBridgeSecret = randGen.nextInt(Integer.MAX_VALUE); + return expectedBridgeSecret; + } + + public void reset() { + jsMessageQueue.reset(); + clearBridgeSecret(); + } + + public String promptOnJsPrompt(String origin, String message, String defaultValue) { + if (defaultValue != null && defaultValue.startsWith("gap:")) { + JSONArray array; + try { + array = new JSONArray(defaultValue.substring(4)); + int bridgeSecret = array.getInt(0); + String service = array.getString(1); + String action = array.getString(2); + String callbackId = array.getString(3); + String r = jsExec(bridgeSecret, service, action, callbackId, message); + return r == null ? "" : r; + } catch (JSONException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return ""; + } + // Sets the native->JS bridge mode. + else if (defaultValue != null && defaultValue.startsWith("gap_bridge_mode:")) { + try { + int bridgeSecret = Integer.parseInt(defaultValue.substring(16)); + jsSetNativeToJsBridgeMode(bridgeSecret, Integer.parseInt(message)); + } catch (NumberFormatException e){ + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return ""; + } + // Polling for JavaScript messages + else if (defaultValue != null && defaultValue.startsWith("gap_poll:")) { + int bridgeSecret = Integer.parseInt(defaultValue.substring(9)); + try { + String r = jsRetrieveJsMessages(bridgeSecret, "1".equals(message)); + return r == null ? "" : r; + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return ""; + } + else if (defaultValue != null && defaultValue.startsWith("gap_init:")) { + // Protect against random iframes being able to talk through the bridge. + // Trust only pages which the app would have been allowed to navigate to anyway. + if (pluginManager.shouldAllowBridgeAccess(origin)) { + // Enable the bridge + int bridgeMode = Integer.parseInt(defaultValue.substring(9)); + jsMessageQueue.setBridgeMode(bridgeMode); + // Tell JS the bridge secret. + int secret = generateBridgeSecret(); + return ""+secret; + } else { + LOG.e(LOG_TAG, "gap_init called from restricted origin: " + origin); + } + return ""; + } + return null; + } +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaClientCertRequest.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaClientCertRequest.java new file mode 100644 index 00000000..ad7c588a --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaClientCertRequest.java @@ -0,0 +1,105 @@ +/* + 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 org.apache.cordova; + +import java.security.Principal; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +import android.annotation.SuppressLint; +import android.webkit.ClientCertRequest; + +/** + * Implementation of the ICordovaClientCertRequest for Android WebView. + * + */ +public class CordovaClientCertRequest implements ICordovaClientCertRequest { + + private final ClientCertRequest request; + + public CordovaClientCertRequest(ClientCertRequest request) { + this.request = request; + } + + /** + * Cancel this request + */ + @SuppressLint("NewApi") + public void cancel() + { + request.cancel(); + } + + /* + * Returns the host name of the server requesting the certificate. + */ + @SuppressLint("NewApi") + public String getHost() + { + return request.getHost(); + } + + /* + * Returns the acceptable types of asymmetric keys (can be null). + */ + @SuppressLint("NewApi") + public String[] getKeyTypes() + { + return request.getKeyTypes(); + } + + /* + * Returns the port number of the server requesting the certificate. + */ + @SuppressLint("NewApi") + public int getPort() + { + return request.getPort(); + } + + /* + * Returns the acceptable certificate issuers for the certificate matching the private key (can be null). + */ + @SuppressLint("NewApi") + public Principal[] getPrincipals() + { + return request.getPrincipals(); + } + + /* + * Ignore the request for now. Do not remember user's choice. + */ + @SuppressLint("NewApi") + public void ignore() + { + request.ignore(); + } + + /* + * Proceed with the specified private key and client certificate chain. Remember the user's positive choice and use it for future requests. + * + * @param privateKey The privateKey + * @param chain The certificate chain + */ + @SuppressLint("NewApi") + public void proceed(PrivateKey privateKey, X509Certificate[] chain) + { + request.proceed(privateKey, chain); + } +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaDialogsHelper.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaDialogsHelper.java new file mode 100644 index 00000000..a219c992 --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaDialogsHelper.java @@ -0,0 +1,152 @@ +/* + 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 org.apache.cordova; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.view.KeyEvent; +import android.widget.EditText; + +/** + * Helper class for WebViews to implement prompt(), alert(), confirm() dialogs. + */ +public class CordovaDialogsHelper { + private final Context context; + private AlertDialog lastHandledDialog; + + public CordovaDialogsHelper(Context context) { + this.context = context; + } + + public void showAlert(String message, final Result result) { + AlertDialog.Builder dlg = new AlertDialog.Builder(context); + dlg.setMessage(message); + dlg.setTitle("Alert"); + //Don't let alerts break the back button + dlg.setCancelable(true); + dlg.setPositiveButton(android.R.string.ok, + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + result.gotResult(true, null); + } + }); + dlg.setOnCancelListener( + new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + result.gotResult(false, null); + } + }); + dlg.setOnKeyListener(new DialogInterface.OnKeyListener() { + //DO NOTHING + public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) + { + result.gotResult(true, null); + return false; + } + else + return true; + } + }); + lastHandledDialog = dlg.show(); + } + + public void showConfirm(String message, final Result result) { + AlertDialog.Builder dlg = new AlertDialog.Builder(context); + dlg.setMessage(message); + dlg.setTitle("Confirm"); + dlg.setCancelable(true); + dlg.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + result.gotResult(true, null); + } + }); + dlg.setNegativeButton(android.R.string.cancel, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + result.gotResult(false, null); + } + }); + dlg.setOnCancelListener( + new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + result.gotResult(false, null); + } + }); + dlg.setOnKeyListener(new DialogInterface.OnKeyListener() { + //DO NOTHING + public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) + { + result.gotResult(false, null); + return false; + } + else + return true; + } + }); + lastHandledDialog = dlg.show(); + } + + /** + * Tell the client to display a prompt dialog to the user. + * If the client returns true, WebView will assume that the client will + * handle the prompt dialog and call the appropriate JsPromptResult method. + * + * Since we are hacking prompts for our own purposes, we should not be using them for + * this purpose, perhaps we should hack console.log to do this instead! + */ + public void showPrompt(String message, String defaultValue, final Result result) { + // Returning false would also show a dialog, but the default one shows the origin (ugly). + AlertDialog.Builder dlg = new AlertDialog.Builder(context); + dlg.setMessage(message); + final EditText input = new EditText(context); + if (defaultValue != null) { + input.setText(defaultValue); + } + dlg.setView(input); + dlg.setCancelable(false); + dlg.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + String userText = input.getText().toString(); + result.gotResult(true, userText); + } + }); + dlg.setNegativeButton(android.R.string.cancel, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + result.gotResult(false, null); + } + }); + lastHandledDialog = dlg.show(); + } + + public void destroyLastDialog(){ + if (lastHandledDialog != null){ + lastHandledDialog.cancel(); + } + } + + public interface Result { + public void gotResult(boolean success, String value); + } +} \ No newline at end of file diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaHttpAuthHandler.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaHttpAuthHandler.java new file mode 100644 index 00000000..a2692f8c --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaHttpAuthHandler.java @@ -0,0 +1,51 @@ +/* + 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 org.apache.cordova; + +import android.webkit.HttpAuthHandler; + +/** + * Specifies interface for HTTP auth handler object which is used to handle auth requests and + * specifying user credentials. + */ +public class CordovaHttpAuthHandler implements ICordovaHttpAuthHandler { + + private final HttpAuthHandler handler; + + public CordovaHttpAuthHandler(HttpAuthHandler handler) { + this.handler = handler; + } + + /** + * Instructs the WebView to cancel the authentication request. + */ + public void cancel () { + this.handler.cancel(); + } + + /** + * Instructs the WebView to proceed with the authentication with the given credentials. + * + * @param username + * @param password + */ + public void proceed (String username, String password) { + this.handler.proceed(username, password); + } +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaInterface.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaInterface.java new file mode 100755 index 00000000..53d03a5c --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaInterface.java @@ -0,0 +1,96 @@ +/* + 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 org.apache.cordova; + +import android.content.Context; +import android.content.Intent; + +import androidx.appcompat.app.AppCompatActivity; + +import java.util.concurrent.ExecutorService; + +/** + * The Activity interface that is implemented by CordovaActivity. + * It is used to isolate plugin development, and remove dependency on entire Cordova library. + */ +public interface CordovaInterface { + + /** + * Launch an activity for which you would like a result when it finished. When this activity exits, + * your onActivityResult() method will be called. + * + * @param command The command object + * @param intent The intent to start + * @param requestCode The request code that is passed to callback to identify the activity + */ + abstract public void startActivityForResult(CordovaPlugin command, Intent intent, int requestCode); + + /** + * Set the plugin to be called when a sub-activity exits. + * + * @param plugin The plugin on which onActivityResult is to be called + */ + abstract public void setActivityResultCallback(CordovaPlugin plugin); + + /** + * Get the Android activity. + * + * If a custom engine lives outside of the Activity's lifecycle the return value may be null. + * + * @return the Activity + */ + public abstract AppCompatActivity getActivity(); + + /** + * Get the Android context. + * + * @return the Context + */ + public Context getContext(); + + /** + * Called when a message is sent to plugin. + * + * @param id The message id + * @param data The message data + * @return Object or null + */ + public Object onMessage(String id, Object data); + + /** + * Returns a shared thread pool that can be used for background tasks. + */ + public ExecutorService getThreadPool(); + + /** + * Sends a permission request to the activity for one permission. + */ + public void requestPermission(CordovaPlugin plugin, int requestCode, String permission); + + /** + * Sends a permission request to the activity for a group of permissions + */ + public void requestPermissions(CordovaPlugin plugin, int requestCode, String [] permissions); + + /** + * Check for a permission. Returns true if the permission is granted, false otherwise. + */ + public boolean hasPermission(String permission); + +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaInterfaceImpl.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaInterfaceImpl.java new file mode 100644 index 00000000..eccc9663 --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaInterfaceImpl.java @@ -0,0 +1,242 @@ +/* + 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 org.apache.cordova; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Bundle; +import android.util.Pair; + +import androidx.appcompat.app.AppCompatActivity; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Default implementation of CordovaInterface. + */ +public class CordovaInterfaceImpl implements CordovaInterface { + private static final String TAG = "CordovaInterfaceImpl"; + protected AppCompatActivity activity; + protected ExecutorService threadPool; + protected PluginManager pluginManager; + + protected ActivityResultHolder savedResult; + protected CallbackMap permissionResultCallbacks; + protected CordovaPlugin activityResultCallback; + protected String initCallbackService; + protected int activityResultRequestCode; + protected boolean activityWasDestroyed = false; + protected Bundle savedPluginState; + + public CordovaInterfaceImpl(AppCompatActivity activity) { + this(activity, Executors.newCachedThreadPool()); + } + + public CordovaInterfaceImpl(AppCompatActivity activity, ExecutorService threadPool) { + this.activity = activity; + this.threadPool = threadPool; + this.permissionResultCallbacks = new CallbackMap(); + } + + @Override + public void startActivityForResult(CordovaPlugin command, Intent intent, int requestCode) { + setActivityResultCallback(command); + try { + activity.startActivityForResult(intent, requestCode); + } catch (RuntimeException e) { // E.g.: ActivityNotFoundException + activityResultCallback = null; + throw e; + } + } + + @Override + public void setActivityResultCallback(CordovaPlugin plugin) { + // Cancel any previously pending activity. + if (activityResultCallback != null) { + activityResultCallback.onActivityResult(activityResultRequestCode, AppCompatActivity.RESULT_CANCELED, null); + } + activityResultCallback = plugin; + } + + @Override + public AppCompatActivity getActivity() { + return activity; + } + + @Override + public Context getContext() { + return activity; + } + + @Override + public Object onMessage(String id, Object data) { + if ("exit".equals(id)) { + activity.finish(); + } + return null; + } + + @Override + public ExecutorService getThreadPool() { + return threadPool; + } + + /** + * Dispatches any pending onActivityResult callbacks and sends the resume event if the + * Activity was destroyed by the OS. + */ + public void onCordovaInit(PluginManager pluginManager) { + this.pluginManager = pluginManager; + if (savedResult != null) { + onActivityResult(savedResult.requestCode, savedResult.resultCode, savedResult.intent); + } else if(activityWasDestroyed) { + // If there was no Activity result, we still need to send out the resume event if the + // Activity was destroyed by the OS + activityWasDestroyed = false; + if(pluginManager != null) + { + CoreAndroid appPlugin = (CoreAndroid) pluginManager.getPlugin(CoreAndroid.PLUGIN_NAME); + if(appPlugin != null) { + JSONObject obj = new JSONObject(); + try { + obj.put("action", "resume"); + } catch (JSONException e) { + LOG.e(TAG, "Failed to create event message", e); + } + appPlugin.sendResumeEvent(new PluginResult(PluginResult.Status.OK, obj)); + } + } + + } + } + + /** + * Routes the result to the awaiting plugin. Returns false if no plugin was waiting. + */ + public boolean onActivityResult(int requestCode, int resultCode, Intent intent) { + CordovaPlugin callback = activityResultCallback; + if(callback == null && initCallbackService != null) { + // The application was restarted, but had defined an initial callback + // before being shut down. + savedResult = new ActivityResultHolder(requestCode, resultCode, intent); + if (pluginManager != null) { + callback = pluginManager.getPlugin(initCallbackService); + if(callback != null) { + callback.onRestoreStateForActivityResult(savedPluginState.getBundle(callback.getServiceName()), + new ResumeCallback(callback.getServiceName(), pluginManager)); + } + } + } + activityResultCallback = null; + + if (callback != null) { + LOG.d(TAG, "Sending activity result to plugin"); + initCallbackService = null; + savedResult = null; + callback.onActivityResult(requestCode, resultCode, intent); + return true; + } + LOG.w(TAG, "Got an activity result, but no plugin was registered to receive it" + (savedResult != null ? " yet!" : ".")); + return false; + } + + /** + * Call this from your startActivityForResult() overload. This is required to catch the case + * where plugins use Activity.startActivityForResult() + CordovaInterface.setActivityResultCallback() + * rather than CordovaInterface.startActivityForResult(). + */ + public void setActivityResultRequestCode(int requestCode) { + activityResultRequestCode = requestCode; + } + + /** + * Saves parameters for startActivityForResult(). + */ + public void onSaveInstanceState(Bundle outState) { + if (activityResultCallback != null) { + String serviceName = activityResultCallback.getServiceName(); + outState.putString("callbackService", serviceName); + } + if(pluginManager != null){ + outState.putBundle("plugin", pluginManager.onSaveInstanceState()); + } + + } + + /** + * Call this from onCreate() so that any saved startActivityForResult parameters will be restored. + */ + public void restoreInstanceState(Bundle savedInstanceState) { + initCallbackService = savedInstanceState.getString("callbackService"); + savedPluginState = savedInstanceState.getBundle("plugin"); + activityWasDestroyed = true; + } + + private static class ActivityResultHolder { + private int requestCode; + private int resultCode; + private Intent intent; + + public ActivityResultHolder(int requestCode, int resultCode, Intent intent) { + this.requestCode = requestCode; + this.resultCode = resultCode; + this.intent = intent; + } + } + + /** + * Called by the system when the user grants permissions + * + * @param requestCode + * @param permissions + * @param grantResults + */ + public void onRequestPermissionResult(int requestCode, String[] permissions, + int[] grantResults) throws JSONException { + Pair callback = permissionResultCallbacks.getAndRemoveCallback(requestCode); + if(callback != null) { + callback.first.onRequestPermissionResult(callback.second, permissions, grantResults); + } + } + + public void requestPermission(CordovaPlugin plugin, int requestCode, String permission) { + String[] permissions = new String [1]; + permissions[0] = permission; + requestPermissions(plugin, requestCode, permissions); + } + + @SuppressLint("NewApi") + public void requestPermissions(CordovaPlugin plugin, int requestCode, String [] permissions) { + int mappedRequestCode = permissionResultCallbacks.registerCallback(plugin, requestCode); + getActivity().requestPermissions(permissions, mappedRequestCode); + } + + public boolean hasPermission(String permission) + { + return PackageManager.PERMISSION_GRANTED == activity.checkSelfPermission(permission); + } +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaPluginPathHandler.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaPluginPathHandler.java new file mode 100644 index 00000000..0acaacb2 --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaPluginPathHandler.java @@ -0,0 +1,38 @@ +/* + 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 org.apache.cordova; + +import androidx.webkit.WebViewAssetLoader; + +/** + * Wrapper class for path and handler + */ +public class CordovaPluginPathHandler { + + private final WebViewAssetLoader.PathHandler handler; + + public CordovaPluginPathHandler(WebViewAssetLoader.PathHandler handler) { + this.handler = handler; + } + + public WebViewAssetLoader.PathHandler getPathHandler() { + return handler; + } +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaPreferences.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaPreferences.java new file mode 100644 index 00000000..96c219c9 --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaPreferences.java @@ -0,0 +1,101 @@ +/* + 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 org.apache.cordova; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import org.apache.cordova.LOG; + +import android.app.Activity; +import android.os.Bundle; + +public class CordovaPreferences { + private HashMap prefs = new HashMap(20); + private Bundle preferencesBundleExtras; + + public void setPreferencesBundle(Bundle extras) { + preferencesBundleExtras = extras; + } + + public void set(String name, String value) { + prefs.put(name.toLowerCase(Locale.ENGLISH), value); + } + + public void set(String name, boolean value) { + set(name, "" + value); + } + + public void set(String name, int value) { + set(name, "" + value); + } + + public void set(String name, double value) { + set(name, "" + value); + } + + public Map getAll() { + return prefs; + } + + public boolean getBoolean(String name, boolean defaultValue) { + name = name.toLowerCase(Locale.ENGLISH); + String value = prefs.get(name); + if (value != null) { + return Boolean.parseBoolean(value); + } + return defaultValue; + } + + // Added in 4.0.0 + public boolean contains(String name) { + return getString(name, null) != null; + } + + public int getInteger(String name, int defaultValue) { + name = name.toLowerCase(Locale.ENGLISH); + String value = prefs.get(name); + if (value != null) { + // Use Integer.decode() can't handle it if the highest bit is set. + return (int)(long)Long.decode(value); + } + return defaultValue; + } + + public double getDouble(String name, double defaultValue) { + name = name.toLowerCase(Locale.ENGLISH); + String value = prefs.get(name); + if (value != null) { + return Double.valueOf(value); + } + return defaultValue; + } + + public String getString(String name, String defaultValue) { + name = name.toLowerCase(Locale.ENGLISH); + String value = prefs.get(name); + if (value != null) { + return value; + } + return defaultValue; + } + +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaResourceApi.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaResourceApi.java new file mode 100644 index 00000000..a9f3453c --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaResourceApi.java @@ -0,0 +1,479 @@ +/* + 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 org.apache.cordova; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.database.Cursor; +import android.net.Uri; +import android.os.Looper; +import android.util.Base64; +import android.webkit.MimeTypeMap; + +import java.io.ByteArrayInputStream; +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; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.channels.FileChannel; +import java.util.Locale; +import java.util.zip.GZIPInputStream; + +/** + * What this class provides: + * 1. Helpers for reading & writing to URLs. + * - E.g. handles assets, resources, content providers, files, data URIs, http[s] + * - E.g. Can be used to query for mime-type & content length. + * + * 2. To allow plugins to redirect URLs (via remapUrl). + * - All plugins should call remapUrl() on URLs they receive from JS *before* + * passing the URL onto other utility functions in this class. + * - For an example usage of this, refer to the org.apache.cordova.file plugin. + * + * Future Work: + * - Consider using a Cursor to query content URLs for their size (like the file plugin does). + * - Allow plugins to remapUri to "cdv-plugin://plugin-name/foo", which CordovaResourceApi + * would then delegate to pluginManager.getPlugin(plugin-name).openForRead(url) + * - Currently, plugins *can* do this by remapping to a data: URL, but it's inefficient + * for large payloads. + */ +public class CordovaResourceApi { + @SuppressWarnings("unused") + private static final String LOG_TAG = "CordovaResourceApi"; + + public static final int URI_TYPE_FILE = 0; + public static final int URI_TYPE_ASSET = 1; + public static final int URI_TYPE_CONTENT = 2; + public static final int URI_TYPE_RESOURCE = 3; + public static final int URI_TYPE_DATA = 4; + public static final int URI_TYPE_HTTP = 5; + public static final int URI_TYPE_HTTPS = 6; + public static final int URI_TYPE_PLUGIN = 7; + public static final int URI_TYPE_UNKNOWN = -1; + + public static final String PLUGIN_URI_SCHEME = "cdvplugin"; + + private static final String[] LOCAL_FILE_PROJECTION = { "_data" }; + + public static Thread jsThread; + + private final AssetManager assetManager; + private final ContentResolver contentResolver; + private final PluginManager pluginManager; + private boolean threadCheckingEnabled = true; + + + public CordovaResourceApi(Context context, PluginManager pluginManager) { + this.contentResolver = context.getContentResolver(); + this.assetManager = context.getAssets(); + this.pluginManager = pluginManager; + } + + public void setThreadCheckingEnabled(boolean value) { + threadCheckingEnabled = value; + } + + public boolean isThreadCheckingEnabled() { + return threadCheckingEnabled; + } + + + public static int getUriType(Uri uri) { + assertNonRelative(uri); + String scheme = uri.getScheme(); + if (ContentResolver.SCHEME_CONTENT.equalsIgnoreCase(scheme)) { + return URI_TYPE_CONTENT; + } + if (ContentResolver.SCHEME_ANDROID_RESOURCE.equalsIgnoreCase(scheme)) { + return URI_TYPE_RESOURCE; + } + if (ContentResolver.SCHEME_FILE.equalsIgnoreCase(scheme)) { + if (uri.getPath().startsWith("/android_asset/")) { + return URI_TYPE_ASSET; + } + return URI_TYPE_FILE; + } + if ("data".equalsIgnoreCase(scheme)) { + return URI_TYPE_DATA; + } + if ("http".equalsIgnoreCase(scheme)) { + return URI_TYPE_HTTP; + } + if ("https".equalsIgnoreCase(scheme)) { + return URI_TYPE_HTTPS; + } + if (PLUGIN_URI_SCHEME.equalsIgnoreCase(scheme)) { + return URI_TYPE_PLUGIN; + } + return URI_TYPE_UNKNOWN; + } + + public Uri remapUri(Uri uri) { + assertNonRelative(uri); + Uri pluginUri = pluginManager.remapUri(uri); + return pluginUri != null ? pluginUri : uri; + } + + public String remapPath(String path) { + return remapUri(Uri.fromFile(new File(path))).getPath(); + } + + /** + * Returns a File that points to the resource, or null if the resource + * is not on the local filesystem. + */ + public File mapUriToFile(Uri uri) { + assertBackgroundThread(); + switch (getUriType(uri)) { + case URI_TYPE_FILE: + return new File(uri.getPath()); + case URI_TYPE_CONTENT: { + Cursor cursor = contentResolver.query(uri, LOCAL_FILE_PROJECTION, null, null, null); + if (cursor != null) { + try { + int columnIndex = cursor.getColumnIndex(LOCAL_FILE_PROJECTION[0]); + if (columnIndex != -1 && cursor.getCount() > 0) { + cursor.moveToFirst(); + String realPath = cursor.getString(columnIndex); + if (realPath != null) { + return new File(realPath); + } + } + } finally { + cursor.close(); + } + } + } + } + return null; + } + + public String getMimeType(Uri uri) { + switch (getUriType(uri)) { + case URI_TYPE_FILE: + case URI_TYPE_ASSET: + return getMimeTypeFromPath(uri.getPath()); + case URI_TYPE_CONTENT: + case URI_TYPE_RESOURCE: + return contentResolver.getType(uri); + case URI_TYPE_DATA: { + return getDataUriMimeType(uri); + } + case URI_TYPE_HTTP: + case URI_TYPE_HTTPS: { + try { + HttpURLConnection conn = (HttpURLConnection)new URL(uri.toString()).openConnection(); + conn.setDoInput(false); + conn.setRequestMethod("HEAD"); + String mimeType = conn.getHeaderField("Content-Type"); + if (mimeType != null) { + mimeType = mimeType.split(";")[0]; + } + return mimeType; + } catch (IOException e) { + } + } + } + + return null; + } + + + //This already exists + private String getMimeTypeFromPath(String path) { + String extension = path; + int lastDot = extension.lastIndexOf('.'); + if (lastDot != -1) { + extension = extension.substring(lastDot + 1); + } + // Convert the URI string to lower case to ensure compatibility with MimeTypeMap (see CB-2185). + extension = extension.toLowerCase(Locale.getDefault()); + if (extension.equals("3ga")) { + return "audio/3gpp"; + } else if (extension.equals("js")) { + // Missing from the map :(. + return "text/javascript"; + } + return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); + } + + /** + * Opens a stream to the given URI, also providing the MIME type & length. + * @return Never returns null. + * @throws Throws an InvalidArgumentException for relative URIs. Relative URIs should be + * resolved before being passed into this function. + * @throws Throws an IOException if the URI cannot be opened. + * @throws Throws an IllegalStateException if called on a foreground thread. + */ + public OpenForReadResult openForRead(Uri uri) throws IOException { + return openForRead(uri, false); + } + + /** + * Opens a stream to the given URI, also providing the MIME type & length. + * @return Never returns null. + * @throws Throws an InvalidArgumentException for relative URIs. Relative URIs should be + * resolved before being passed into this function. + * @throws Throws an IOException if the URI cannot be opened. + * @throws Throws an IllegalStateException if called on a foreground thread and skipThreadCheck is false. + */ + public OpenForReadResult openForRead(Uri uri, boolean skipThreadCheck) throws IOException { + if (!skipThreadCheck) { + assertBackgroundThread(); + } + switch (getUriType(uri)) { + case URI_TYPE_FILE: { + FileInputStream inputStream = new FileInputStream(uri.getPath()); + String mimeType = getMimeTypeFromPath(uri.getPath()); + long length = inputStream.getChannel().size(); + return new OpenForReadResult(uri, inputStream, mimeType, length, null); + } + case URI_TYPE_ASSET: { + String assetPath = uri.getPath().substring(15); + AssetFileDescriptor assetFd = null; + InputStream inputStream; + long length = -1; + try { + assetFd = assetManager.openFd(assetPath); + inputStream = assetFd.createInputStream(); + length = assetFd.getLength(); + } catch (FileNotFoundException e) { + // Will occur if the file is compressed. + inputStream = assetManager.open(assetPath); + length = inputStream.available(); + } + String mimeType = getMimeTypeFromPath(assetPath); + return new OpenForReadResult(uri, inputStream, mimeType, length, assetFd); + } + case URI_TYPE_CONTENT: + case URI_TYPE_RESOURCE: { + String mimeType = contentResolver.getType(uri); + AssetFileDescriptor assetFd = contentResolver.openAssetFileDescriptor(uri, "r"); + InputStream inputStream = assetFd.createInputStream(); + long length = assetFd.getLength(); + return new OpenForReadResult(uri, inputStream, mimeType, length, assetFd); + } + case URI_TYPE_DATA: { + OpenForReadResult ret = readDataUri(uri); + if (ret == null) { + break; + } + return ret; + } + case URI_TYPE_HTTP: + case URI_TYPE_HTTPS: { + HttpURLConnection conn = (HttpURLConnection)new URL(uri.toString()).openConnection(); + conn.setRequestProperty("Accept-Encoding", "gzip"); + conn.setDoInput(true); + String mimeType = conn.getHeaderField("Content-Type"); + if (mimeType != null) { + mimeType = mimeType.split(";")[0]; + } + int length = conn.getContentLength(); + InputStream inputStream; + if ("gzip".equals(conn.getContentEncoding())) { + inputStream = new GZIPInputStream(conn.getInputStream()); + } else { + inputStream = conn.getInputStream(); + } + return new OpenForReadResult(uri, inputStream, mimeType, length, null); + } + case URI_TYPE_PLUGIN: { + String pluginId = uri.getHost(); + CordovaPlugin plugin = pluginManager.getPlugin(pluginId); + if (plugin == null) { + throw new FileNotFoundException("Invalid plugin ID in URI: " + uri); + } + return plugin.handleOpenForRead(uri); + } + } + throw new FileNotFoundException("URI not supported by CordovaResourceApi: " + uri); + } + + public OutputStream openOutputStream(Uri uri) throws IOException { + return openOutputStream(uri, false); + } + + /** + * Opens a stream to the given URI. + * @return Never returns null. + * @throws Throws an InvalidArgumentException for relative URIs. Relative URIs should be + * resolved before being passed into this function. + * @throws Throws an IOException if the URI cannot be opened. + */ + public OutputStream openOutputStream(Uri uri, boolean append) throws IOException { + assertBackgroundThread(); + switch (getUriType(uri)) { + case URI_TYPE_FILE: { + File localFile = new File(uri.getPath()); + File parent = localFile.getParentFile(); + if (parent != null) { + parent.mkdirs(); + } + return new FileOutputStream(localFile, append); + } + case URI_TYPE_CONTENT: + case URI_TYPE_RESOURCE: { + AssetFileDescriptor assetFd = contentResolver.openAssetFileDescriptor(uri, append ? "wa" : "w"); + return assetFd.createOutputStream(); + } + } + throw new FileNotFoundException("URI not supported by CordovaResourceApi: " + uri); + } + + public HttpURLConnection createHttpConnection(Uri uri) throws IOException { + assertBackgroundThread(); + return (HttpURLConnection)new URL(uri.toString()).openConnection(); + } + + // Copies the input to the output in the most efficient manner possible. + // Closes both streams. + public void copyResource(OpenForReadResult input, OutputStream outputStream) throws IOException { + assertBackgroundThread(); + try { + InputStream inputStream = input.inputStream; + if (inputStream instanceof FileInputStream && outputStream instanceof FileOutputStream) { + FileChannel inChannel = ((FileInputStream)input.inputStream).getChannel(); + FileChannel outChannel = ((FileOutputStream)outputStream).getChannel(); + long offset = 0; + long length = input.length; + if (input.assetFd != null) { + offset = input.assetFd.getStartOffset(); + } + // transferFrom()'s 2nd arg is a relative position. Need to set the absolute + // position first. + inChannel.position(offset); + outChannel.transferFrom(inChannel, 0, length); + } else { + final int BUFFER_SIZE = 8192; + byte[] buffer = new byte[BUFFER_SIZE]; + + for (;;) { + int bytesRead = inputStream.read(buffer, 0, BUFFER_SIZE); + + if (bytesRead <= 0) { + break; + } + outputStream.write(buffer, 0, bytesRead); + } + } + } finally { + input.inputStream.close(); + if (outputStream != null) { + outputStream.close(); + } + } + } + + public void copyResource(Uri sourceUri, OutputStream outputStream) throws IOException { + copyResource(openForRead(sourceUri), outputStream); + } + + // Added in 3.5.0. + public void copyResource(Uri sourceUri, Uri dstUri) throws IOException { + copyResource(openForRead(sourceUri), openOutputStream(dstUri)); + } + + private void assertBackgroundThread() { + if (threadCheckingEnabled) { + Thread curThread = Thread.currentThread(); + if (curThread == Looper.getMainLooper().getThread()) { + throw new IllegalStateException("Do not perform IO operations on the UI thread. Use CordovaInterface.getThreadPool() instead."); + } + if (curThread == jsThread) { + throw new IllegalStateException("Tried to perform an IO operation on the WebCore thread. Use CordovaInterface.getThreadPool() instead."); + } + } + } + + private String getDataUriMimeType(Uri uri) { + String uriAsString = uri.getSchemeSpecificPart(); + int commaPos = uriAsString.indexOf(','); + if (commaPos == -1) { + return null; + } + String[] mimeParts = uriAsString.substring(0, commaPos).split(";"); + if (mimeParts.length > 0) { + return mimeParts[0]; + } + return null; + } + + private OpenForReadResult readDataUri(Uri uri) { + String uriAsString = uri.getSchemeSpecificPart(); + int commaPos = uriAsString.indexOf(','); + if (commaPos == -1) { + return null; + } + String[] mimeParts = uriAsString.substring(0, commaPos).split(";"); + String contentType = null; + boolean base64 = false; + if (mimeParts.length > 0) { + contentType = mimeParts[0]; + } + for (int i = 1; i < mimeParts.length; ++i) { + if ("base64".equalsIgnoreCase(mimeParts[i])) { + base64 = true; + } + } + String dataPartAsString = uriAsString.substring(commaPos + 1); + byte[] data; + if (base64) { + data = Base64.decode(dataPartAsString, Base64.DEFAULT); + } else { + try { + data = dataPartAsString.getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + data = dataPartAsString.getBytes(); + } + } + InputStream inputStream = new ByteArrayInputStream(data); + return new OpenForReadResult(uri, inputStream, contentType, data.length, null); + } + + private static void assertNonRelative(Uri uri) { + if (!uri.isAbsolute()) { + throw new IllegalArgumentException("Relative URIs are not supported."); + } + } + + public static final class OpenForReadResult { + public final Uri uri; + public final InputStream inputStream; + public final String mimeType; + public final long length; + public final AssetFileDescriptor assetFd; + + public OpenForReadResult(Uri uri, InputStream inputStream, String mimeType, long length, AssetFileDescriptor assetFd) { + this.uri = uri; + this.inputStream = inputStream; + this.mimeType = mimeType; + this.length = length; + this.assetFd = assetFd; + } + } +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaWebView.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaWebView.java new file mode 100644 index 00000000..fc890a44 --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaWebView.java @@ -0,0 +1,142 @@ +/* + 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 org.apache.cordova; + +import java.util.List; +import java.util.Map; + +import android.content.Context; +import android.content.Intent; +import android.view.View; +import android.webkit.WebChromeClient.CustomViewCallback; + +/** + * Main interface for interacting with a Cordova webview - implemented by CordovaWebViewImpl. + * This is an interface so that it can be easily mocked in tests. + * Methods may be added to this interface without a major version bump, as plugins & embedders + * are not expected to implement it. + */ +public interface CordovaWebView { + public static final String CORDOVA_VERSION = "12.0.1"; + + void init(CordovaInterface cordova, List pluginEntries, CordovaPreferences preferences); + + boolean isInitialized(); + + View getView(); + + void loadUrlIntoView(String url, boolean recreatePlugins); + + void stopLoading(); + + boolean canGoBack(); + + void clearCache(); + + /** Use parameter-less overload */ + @Deprecated + void clearCache(boolean b); + + void clearHistory(); + + boolean backHistory(); + + void handlePause(boolean keepRunning); + + void onNewIntent(Intent intent); + + void handleResume(boolean keepRunning); + + void handleStart(); + + void handleStop(); + + void handleDestroy(); + + /** + * Send JavaScript statement back to JavaScript. + * + * Deprecated (https://issues.apache.org/jira/browse/CB-6851) + * Instead of executing snippets of JS, you should use the exec bridge + * to create a Java->JS communication channel. + * To do this: + * 1. Within plugin.xml (to have your JS run before deviceready): + * + * 2. Within your .js (call exec on start-up): + * require('cordova/channel').onCordovaReady.subscribe(function() { + * require('cordova/exec')(win, null, 'Plugin', 'method', []); + * function win(message) { + * ... process message from java here ... + * } + * }); + * 3. Within your .java: + * PluginResult dataResult = new PluginResult(PluginResult.Status.OK, CODE); + * dataResult.setKeepCallback(true); + * savedCallbackContext.sendPluginResult(dataResult); + */ + @Deprecated + void sendJavascript(String statememt); + + /** + * Load the specified URL in the Cordova webview or a new browser instance. + * + * NOTE: If openExternal is false, only allow listed URLs can be loaded. + * + * @param url The url to load. + * @param openExternal Load url in browser instead of Cordova webview. + * @param clearHistory Clear the history stack, so new page becomes top of history + * @param params Parameters for new app + */ + void showWebPage(String url, boolean openExternal, boolean clearHistory, Map params); + + /** + * Deprecated in 4.0.0. Use your own View-toggling logic. + */ + @Deprecated + boolean isCustomViewShowing(); + + /** + * Deprecated in 4.0.0. Use your own View-toggling logic. + */ + @Deprecated + void showCustomView(View view, CustomViewCallback callback); + + /** + * Deprecated in 4.0.0. Use your own View-toggling logic. + */ + @Deprecated + void hideCustomView(); + + CordovaResourceApi getResourceApi(); + + void setButtonPlumbedToJs(int keyCode, boolean override); + boolean isButtonPlumbedToJs(int keyCode); + + void sendPluginResult(PluginResult cr, String callbackId); + + PluginManager getPluginManager(); + CordovaWebViewEngine getEngine(); + CordovaPreferences getPreferences(); + ICordovaCookieManager getCookieManager(); + + String getUrl(); + + // TODO: Work on deleting these by removing refs from plugins. + Context getContext(); + void loadUrl(String url); + Object postMessage(String id, Object data); +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaWebViewEngine.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaWebViewEngine.java new file mode 100644 index 00000000..c8e5a55d --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaWebViewEngine.java @@ -0,0 +1,85 @@ +/* + 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 org.apache.cordova; + +import android.view.KeyEvent; +import android.view.View; +import android.webkit.ValueCallback; + +/** + * Interface for all Cordova engines. + * No methods will be added to this class (in order to be compatible with existing engines). + * Instead, we will create a new interface: e.g. CordovaWebViewEngineV2 + */ +public interface CordovaWebViewEngine { + void init(CordovaWebView parentWebView, CordovaInterface cordova, Client client, + CordovaResourceApi resourceApi, PluginManager pluginManager, + NativeToJsMessageQueue nativeToJsMessageQueue); + + CordovaWebView getCordovaWebView(); + ICordovaCookieManager getCookieManager(); + View getView(); + + void loadUrl(String url, boolean clearNavigationStack); + + void stopLoading(); + + /** Return the currently loaded URL */ + String getUrl(); + + void clearCache(); + + /** After calling clearHistory(), canGoBack() should be false. */ + void clearHistory(); + + boolean canGoBack(); + + /** Returns whether a navigation occurred */ + boolean goBack(); + + /** Pauses / resumes the WebView's event loop. */ + void setPaused(boolean value); + + /** Clean up all resources associated with the WebView. */ + void destroy(); + + /** Add the evaulate Javascript method **/ + void evaluateJavascript(String js, ValueCallback callback); + + /** + * Used to retrieve the associated CordovaWebView given a View without knowing the type of Engine. + * E.g. ((CordovaWebView.EngineView)activity.findViewById(android.R.id.webView)).getCordovaWebView(); + */ + public interface EngineView { + CordovaWebView getCordovaWebView(); + } + + /** + * Contains methods that an engine uses to communicate with the parent CordovaWebView. + * Methods may be added in future cordova versions, but never removed. + */ + public interface Client { + Boolean onDispatchKeyEvent(KeyEvent event); + void clearLoadTimeoutTimer(); + void onPageStarted(String newUrl); + void onReceivedError(int errorCode, String description, String failingUrl); + void onPageFinishedLoading(String url); + boolean onNavigationAttempt(String url); + } +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaWebViewImpl.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaWebViewImpl.java new file mode 100644 index 00000000..1a48f8b1 --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CordovaWebViewImpl.java @@ -0,0 +1,668 @@ +/* + 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 org.apache.cordova; + +import android.annotation.SuppressLint; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.WebChromeClient; +import android.widget.FrameLayout; + +import org.apache.cordova.engine.SystemWebViewEngine; +import org.json.JSONException; +import org.json.JSONObject; + +import java.lang.reflect.Constructor; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Main class for interacting with a Cordova webview. Manages plugins, events, and a CordovaWebViewEngine. + * Class uses two-phase initialization. You must call init() before calling any other methods. + */ +public class CordovaWebViewImpl implements CordovaWebView { + + public static final String TAG = "CordovaWebViewImpl"; + + private PluginManager pluginManager; + + protected final CordovaWebViewEngine engine; + private CordovaInterface cordova; + + // Flag to track that a loadUrl timeout occurred + private int loadUrlTimeout = 0; + + private CordovaResourceApi resourceApi; + private CordovaPreferences preferences; + private CoreAndroid appPlugin; + private NativeToJsMessageQueue nativeToJsMessageQueue; + private EngineClient engineClient = new EngineClient(); + private boolean hasPausedEver; + + // The URL passed to loadUrl(), not necessarily the URL of the current page. + String loadedUrl; + + /** custom view created by the browser (a video player for example) */ + private View mCustomView; + private WebChromeClient.CustomViewCallback mCustomViewCallback; + + private Set boundKeyCodes = new HashSet(); + + public static CordovaWebViewEngine createEngine(Context context, CordovaPreferences preferences) { + String className = preferences.getString("webview", SystemWebViewEngine.class.getCanonicalName()); + try { + Class webViewClass = Class.forName(className); + Constructor constructor = webViewClass.getConstructor(Context.class, CordovaPreferences.class); + return (CordovaWebViewEngine) constructor.newInstance(context, preferences); + } catch (Exception e) { + throw new RuntimeException("Failed to create webview. ", e); + } + } + + public CordovaWebViewImpl(CordovaWebViewEngine cordovaWebViewEngine) { + this.engine = cordovaWebViewEngine; + } + + // Convenience method for when creating programmatically (not from Config.xml). + public void init(CordovaInterface cordova) { + init(cordova, new ArrayList(), new CordovaPreferences()); + } + + @SuppressLint("Assert") + @Override + public void init(CordovaInterface cordova, List pluginEntries, CordovaPreferences preferences) { + if (this.cordova != null) { + throw new IllegalStateException(); + } + this.cordova = cordova; + this.preferences = preferences; + pluginManager = new PluginManager(this, this.cordova, pluginEntries); + resourceApi = new CordovaResourceApi(engine.getView().getContext(), pluginManager); + nativeToJsMessageQueue = new NativeToJsMessageQueue(); + nativeToJsMessageQueue.addBridgeMode(new NativeToJsMessageQueue.NoOpBridgeMode()); + nativeToJsMessageQueue.addBridgeMode(new NativeToJsMessageQueue.LoadUrlBridgeMode(engine, cordova)); + + if (preferences.getBoolean("DisallowOverscroll", false)) { + engine.getView().setOverScrollMode(View.OVER_SCROLL_NEVER); + } + engine.init(this, cordova, engineClient, resourceApi, pluginManager, nativeToJsMessageQueue); + // This isn't enforced by the compiler, so assert here. + assert engine.getView() instanceof CordovaWebViewEngine.EngineView; + + pluginManager.addService(CoreAndroid.PLUGIN_NAME, "org.apache.cordova.CoreAndroid", true); + pluginManager.init(); + } + + @Override + public boolean isInitialized() { + return cordova != null; + } + + @Override + public void loadUrlIntoView(final String url, boolean recreatePlugins) { + LOG.d(TAG, ">>> loadUrl(" + url + ")"); + if (url.equals("about:blank") || url.startsWith("javascript:")) { + engine.loadUrl(url, false); + return; + } + + recreatePlugins = recreatePlugins || (loadedUrl == null); + + if (recreatePlugins) { + // Don't re-initialize on first load. + if (loadedUrl != null) { + appPlugin = null; + pluginManager.init(); + } + loadedUrl = url; + } + + // Create a timeout timer for loadUrl + final int currentLoadUrlTimeout = loadUrlTimeout; + final int loadUrlTimeoutValue = preferences.getInteger("LoadUrlTimeoutValue", 20000); + + // Timeout error method + final Runnable loadError = new Runnable() { + public void run() { + stopLoading(); + LOG.e(TAG, "CordovaWebView: TIMEOUT ERROR!"); + + // Handle other errors by passing them to the webview in JS + JSONObject data = new JSONObject(); + try { + data.put("errorCode", -6); + data.put("description", "The connection to the server was unsuccessful."); + data.put("url", url); + } catch (JSONException e) { + // Will never happen. + } + pluginManager.postMessage("onReceivedError", data); + } + }; + + // Timeout timer method + final Runnable timeoutCheck = new Runnable() { + public void run() { + try { + synchronized (this) { + wait(loadUrlTimeoutValue); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + + // If timeout, then stop loading and handle error (if activity still exists) + if (loadUrlTimeout == currentLoadUrlTimeout && cordova.getActivity() != null) { + cordova.getActivity().runOnUiThread(loadError); + } else if (cordova.getActivity() == null) { + LOG.d(TAG, "Cordova activity does not exist."); + } + } + }; + + if (cordova.getActivity() != null) { + final boolean _recreatePlugins = recreatePlugins; + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + if (loadUrlTimeoutValue > 0) { + cordova.getThreadPool().execute(timeoutCheck); + } + engine.loadUrl(url, _recreatePlugins); + } + }); + } else { + LOG.d(TAG, "Cordova activity does not exist."); + } + } + + + @Override + public void loadUrl(String url) { + loadUrlIntoView(url, true); + } + + @Override + public void showWebPage(String url, boolean openExternal, boolean clearHistory, Map params) { + LOG.d(TAG, "showWebPage(%s, %b, %b, HashMap)", url, openExternal, clearHistory); + + // If clearing history + if (clearHistory) { + engine.clearHistory(); + } + + // If loading into our webview + if (!openExternal) { + // Make sure url is in allow list + if (pluginManager.shouldAllowNavigation(url)) { + // TODO: What about params? + // Load new URL + loadUrlIntoView(url, true); + return; + } else { + LOG.w(TAG, "showWebPage: Refusing to load URL into webview since it is not in the allow list. URL=" + url); + return; + } + } + if (!pluginManager.shouldOpenExternalUrl(url)) { + LOG.w(TAG, "showWebPage: Refusing to send intent for URL since it is not in the allow list. URL=" + url); + return; + } + + Intent intent = null; + try { + if (url.startsWith("intent://")) { + intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); + } else { + intent = new Intent(Intent.ACTION_VIEW); + // To send an intent without CATEGORY_BROWSER, a custom plugin should be used. + intent.addCategory(Intent.CATEGORY_BROWSABLE); + Uri uri = Uri.parse(url); + // Omitting the MIME type for file: URLs causes "No Activity found to handle Intent". + // Adding the MIME type to http: URLs causes them to not be handled by the downloader. + if ("file".equals(uri.getScheme())) { + intent.setDataAndType(uri, resourceApi.getMimeType(uri)); + } else { + intent.setData(uri); + } + } + if (cordova.getActivity() != null) { + cordova.getActivity().startActivity(intent); + } else { + LOG.d(TAG, "Cordova activity does not exist."); + } + } catch (URISyntaxException e) { + LOG.e(TAG, "Error parsing url " + url, e); + } catch (ActivityNotFoundException e) { + if (url.startsWith("intent://") && intent != null && intent.getStringExtra("browser_fallback_url") != null) { + showWebPage(intent.getStringExtra("browser_fallback_url"), openExternal, clearHistory, params); + } else { + LOG.e(TAG, "Error loading url " + url, e); + } + } + } + + private static class WrapperView extends FrameLayout { + + private final CordovaWebViewEngine engine; + + public WrapperView(Context context, CordovaWebViewEngine engine) { + super(context); + this.engine = engine; + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + boolean ret = engine.getView().dispatchKeyEvent(event); + if (!ret) { + // If the engine didn't handle the event, handle it normally. + ret = super.dispatchKeyEvent(event); + } + return ret; + } + } + + @Override + @Deprecated + public void showCustomView(View view, WebChromeClient.CustomViewCallback callback) { + // This code is adapted from the original Android Browser code, licensed under the Apache License, Version 2.0 + LOG.d(TAG, "showing Custom View"); + // if a view already exists then immediately terminate the new one + if (mCustomView != null) { + callback.onCustomViewHidden(); + return; + } + + WrapperView wrapperView = new WrapperView(getContext(), engine); + wrapperView.addView(view); + + // Store the view and its callback for later (to kill it properly) + mCustomView = wrapperView; + mCustomViewCallback = callback; + + // Add the custom view to its container. + ViewGroup parent = (ViewGroup) engine.getView().getParent(); + parent.addView(wrapperView, new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, + Gravity.CENTER)); + + // Hide the content view. + engine.getView().setVisibility(View.GONE); + + // Finally show the custom view container. + parent.setVisibility(View.VISIBLE); + parent.bringToFront(); + } + + @Override + @Deprecated + public void hideCustomView() { + // This code is adapted from the original Android Browser code, licensed under the Apache License, Version 2.0 + if (mCustomView == null) return; + LOG.d(TAG, "Hiding Custom View"); + + // Hide the custom view. + mCustomView.setVisibility(View.GONE); + + // Remove the custom view from its container. + ViewGroup parent = (ViewGroup) engine.getView().getParent(); + parent.removeView(mCustomView); + mCustomView = null; + mCustomViewCallback.onCustomViewHidden(); + + // Show the content view. + engine.getView().setVisibility(View.VISIBLE); + engine.getView().requestFocus(); + } + + @Override + @Deprecated + public boolean isCustomViewShowing() { + return mCustomView != null; + } + + @Override + @Deprecated + public void sendJavascript(String statement) { + nativeToJsMessageQueue.addJavaScript(statement); + } + + @Override + public void sendPluginResult(PluginResult cr, String callbackId) { + nativeToJsMessageQueue.addPluginResult(cr, callbackId); + } + + @Override + public PluginManager getPluginManager() { + return pluginManager; + } + @Override + public CordovaPreferences getPreferences() { + return preferences; + } + @Override + public ICordovaCookieManager getCookieManager() { + return engine.getCookieManager(); + } + @Override + public CordovaResourceApi getResourceApi() { + return resourceApi; + } + @Override + public CordovaWebViewEngine getEngine() { + return engine; + } + @Override + public View getView() { + return engine.getView(); + } + @Override + public Context getContext() { + return engine.getView().getContext(); + } + + private void sendJavascriptEvent(String event) { + if (appPlugin == null) { + appPlugin = (CoreAndroid)pluginManager.getPlugin(CoreAndroid.PLUGIN_NAME); + } + + if (appPlugin == null) { + LOG.w(TAG, "Unable to fire event without existing plugin"); + return; + } + appPlugin.fireJavascriptEvent(event); + } + + @Override + public void setButtonPlumbedToJs(int keyCode, boolean override) { + switch (keyCode) { + case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_BACK: + case KeyEvent.KEYCODE_MENU: + // TODO: Why are search and menu buttons handled separately? + if (override) { + boundKeyCodes.add(keyCode); + } else { + boundKeyCodes.remove(keyCode); + } + return; + default: + throw new IllegalArgumentException("Unsupported keycode: " + keyCode); + } + } + + @Override + public boolean isButtonPlumbedToJs(int keyCode) { + return boundKeyCodes.contains(keyCode); + } + + @Override + public Object postMessage(String id, Object data) { + return pluginManager.postMessage(id, data); + } + + // Engine method proxies: + @Override + public String getUrl() { + return engine.getUrl(); + } + + @Override + public void stopLoading() { + // Clear timeout flag + loadUrlTimeout++; + } + + @Override + public boolean canGoBack() { + return engine.canGoBack(); + } + + @Override + public void clearCache() { + engine.clearCache(); + } + + @Override + @Deprecated + public void clearCache(boolean b) { + engine.clearCache(); + } + + @Override + public void clearHistory() { + engine.clearHistory(); + } + + @Override + public boolean backHistory() { + return engine.goBack(); + } + + /////// LifeCycle methods /////// + @Override + public void onNewIntent(Intent intent) { + if (this.pluginManager != null) { + this.pluginManager.onNewIntent(intent); + } + } + @Override + public void handlePause(boolean keepRunning) { + if (!isInitialized()) { + return; + } + hasPausedEver = true; + pluginManager.onPause(keepRunning); + sendJavascriptEvent("pause"); + + // If app doesn't want to run in background + if (!keepRunning) { + // Pause JavaScript timers. This affects all webviews within the app! + engine.setPaused(true); + } + } + @Override + public void handleResume(boolean keepRunning) { + if (!isInitialized()) { + return; + } + + // Resume JavaScript timers. This affects all webviews within the app! + engine.setPaused(false); + this.pluginManager.onResume(keepRunning); + + // In order to match the behavior of the other platforms, we only send onResume after an + // onPause has occurred. The resume event might still be sent if the Activity was killed + // while waiting for the result of an external Activity once the result is obtained + if (hasPausedEver) { + sendJavascriptEvent("resume"); + } + } + @Override + public void handleStart() { + if (!isInitialized()) { + return; + } + pluginManager.onStart(); + } + @Override + public void handleStop() { + if (!isInitialized()) { + return; + } + pluginManager.onStop(); + } + @Override + public void handleDestroy() { + if (!isInitialized()) { + return; + } + // Cancel pending timeout timer. + loadUrlTimeout++; + + // Forward to plugins + this.pluginManager.onDestroy(); + + // TODO: about:blank is a bit special (and the default URL for new frames) + // We should use a blank data: url instead so it's more obvious + this.loadUrl("about:blank"); + + // TODO: Should not destroy webview until after about:blank is done loading. + engine.destroy(); + hideCustomView(); + } + + protected class EngineClient implements CordovaWebViewEngine.Client { + @Override + public void clearLoadTimeoutTimer() { + loadUrlTimeout++; + } + + @Override + public void onPageStarted(String newUrl) { + LOG.d(TAG, "onPageDidNavigate(" + newUrl + ")"); + boundKeyCodes.clear(); + pluginManager.onReset(); + pluginManager.postMessage("onPageStarted", newUrl); + } + + @Override + public void onReceivedError(int errorCode, String description, String failingUrl) { + clearLoadTimeoutTimer(); + JSONObject data = new JSONObject(); + try { + data.put("errorCode", errorCode); + data.put("description", description); + data.put("url", failingUrl); + } catch (JSONException e) { + e.printStackTrace(); + } + pluginManager.postMessage("onReceivedError", data); + } + + @Override + public void onPageFinishedLoading(String url) { + LOG.d(TAG, "onPageFinished(" + url + ")"); + + clearLoadTimeoutTimer(); + + // Broadcast message that page has loaded + pluginManager.postMessage("onPageFinished", url); + + // Make app visible after 2 sec in case there was a JS error and Cordova JS never initialized correctly + if (engine.getView().getVisibility() != View.VISIBLE) { + Thread t = new Thread(new Runnable() { + public void run() { + try { + Thread.sleep(2000); + if (cordova.getActivity() != null) { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + pluginManager.postMessage("spinner", "stop"); + } + }); + } else { + LOG.d(TAG, "Cordova activity does not exist."); + } + } catch (InterruptedException e) { + } + } + }); + t.start(); + } + + // Shutdown if blank loaded + if (url.equals("about:blank")) { + pluginManager.postMessage("exit", null); + } + } + + @Override + public Boolean onDispatchKeyEvent(KeyEvent event) { + int keyCode = event.getKeyCode(); + boolean isBackButton = keyCode == KeyEvent.KEYCODE_BACK; + if (event.getAction() == KeyEvent.ACTION_DOWN) { + if (isBackButton && mCustomView != null) { + return true; + } else if (boundKeyCodes.contains(keyCode)) { + return true; + } else if (isBackButton) { + return engine.canGoBack(); + } + } else if (event.getAction() == KeyEvent.ACTION_UP) { + if (isBackButton && mCustomView != null) { + hideCustomView(); + return true; + } else if (boundKeyCodes.contains(keyCode)) { + String eventName = null; + switch (keyCode) { + case KeyEvent.KEYCODE_VOLUME_DOWN: + eventName = "volumedownbutton"; + break; + case KeyEvent.KEYCODE_VOLUME_UP: + eventName = "volumeupbutton"; + break; + case KeyEvent.KEYCODE_SEARCH: + eventName = "searchbutton"; + break; + case KeyEvent.KEYCODE_MENU: + eventName = "menubutton"; + break; + case KeyEvent.KEYCODE_BACK: + eventName = "backbutton"; + break; + } + if (eventName != null) { + sendJavascriptEvent(eventName); + return true; + } + } else if (isBackButton) { + return engine.goBack(); + } + } + return null; + } + + @Override + public boolean onNavigationAttempt(String url) { + // Give plugins the chance to handle the url + if (pluginManager.onOverrideUrlLoading(url)) { + return true; + } else if (pluginManager.shouldAllowNavigation(url)) { + return false; + } else if (pluginManager.shouldOpenExternalUrl(url)) { + showWebPage(url, true, false, null); + return true; + } + LOG.w(TAG, "Blocked (possibly sub-frame) navigation to non-allowed URL: " + url); + return true; + } + } +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CoreAndroid.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CoreAndroid.java new file mode 100755 index 00000000..ea04ca4d --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/CoreAndroid.java @@ -0,0 +1,395 @@ +/* + 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 org.apache.cordova; + +import org.apache.cordova.BuildHelper; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.telephony.TelephonyManager; +import android.view.KeyEvent; + +import java.util.HashMap; + +/** + * This class exposes methods in Cordova that can be called from JavaScript. + */ +public class CoreAndroid extends CordovaPlugin { + + public static final String PLUGIN_NAME = "CoreAndroid"; + protected static final String TAG = "CordovaApp"; + private BroadcastReceiver telephonyReceiver; + private CallbackContext messageChannel; + private PluginResult pendingResume; + private PluginResult pendingPause; + private final Object messageChannelLock = new Object(); + + /** + * Send an event to be fired on the Javascript side. + * + * @param action The name of the event to be fired + */ + public void fireJavascriptEvent(String action) { + sendEventMessage(action); + } + + /** + * Sets the context of the Command. This can then be used to do things like + * get file paths associated with the Activity. + */ + @Override + public void pluginInitialize() { + this.initTelephonyReceiver(); + } + + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackContext The callback context from which we were invoked. + * @return A PluginResult object with a status and message. + */ + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + PluginResult.Status status = PluginResult.Status.OK; + String result = ""; + + try { + if (action.equals("clearCache")) { + this.clearCache(); + } + else if (action.equals("show")) { + // This gets called from JavaScript onCordovaReady to show the webview. + // I recommend we change the name of the Message as spinner/stop is not + // indicative of what this actually does (shows the webview). + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + webView.getPluginManager().postMessage("spinner", "stop"); + } + }); + } + else if (action.equals("loadUrl")) { + this.loadUrl(args.getString(0), args.optJSONObject(1)); + } + else if (action.equals("cancelLoadUrl")) { + //this.cancelLoadUrl(); + } + else if (action.equals("clearHistory")) { + this.clearHistory(); + } + else if (action.equals("backHistory")) { + this.backHistory(); + } + else if (action.equals("overrideButton")) { + this.overrideButton(args.getString(0), args.getBoolean(1)); + } + else if (action.equals("overrideBackbutton")) { + this.overrideBackbutton(args.getBoolean(0)); + } + else if (action.equals("exitApp")) { + this.exitApp(); + } + else if (action.equals("messageChannel")) { + synchronized(messageChannelLock) { + messageChannel = callbackContext; + if (pendingPause != null) { + sendEventMessage(pendingPause); + pendingPause = null; + } + if (pendingResume != null) { + sendEventMessage(pendingResume); + pendingResume = null; + } + } + return true; + } + + callbackContext.sendPluginResult(new PluginResult(status, result)); + return true; + } catch (JSONException e) { + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); + return false; + } + } + + //-------------------------------------------------------------------------- + // LOCAL METHODS + //-------------------------------------------------------------------------- + + /** + * Clear the resource cache. + */ + public void clearCache() { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + webView.clearCache(); + } + }); + } + + /** + * Load the url into the webview. + * + * @param url + * @param props Properties that can be passed in to the Cordova activity (i.e. loadingDialog, wait, ...) + * @throws JSONException + */ + public void loadUrl(String url, JSONObject props) throws JSONException { + LOG.d("App", "App.loadUrl("+url+","+props+")"); + int wait = 0; + boolean openExternal = false; + boolean clearHistory = false; + + // If there are properties, then set them on the Activity + HashMap params = new HashMap(); + if (props != null) { + JSONArray keys = props.names(); + for (int i = 0; i < keys.length(); i++) { + String key = keys.getString(i); + if (key.equals("wait")) { + wait = props.getInt(key); + } + else if (key.equalsIgnoreCase("openexternal")) { + openExternal = props.getBoolean(key); + } + else if (key.equalsIgnoreCase("clearhistory")) { + clearHistory = props.getBoolean(key); + } + else { + Object value = props.get(key); + if (value == null) { + + } + else if (value.getClass().equals(String.class)) { + params.put(key, (String)value); + } + else if (value.getClass().equals(Boolean.class)) { + params.put(key, (Boolean)value); + } + else if (value.getClass().equals(Integer.class)) { + params.put(key, (Integer)value); + } + } + } + } + + // If wait property, then delay loading + + if (wait > 0) { + try { + synchronized(this) { + this.wait(wait); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + this.webView.showWebPage(url, openExternal, clearHistory, params); + } + + /** + * Clear page history for the app. + */ + public void clearHistory() { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + webView.clearHistory(); + } + }); + } + + /** + * Go to previous page displayed. + * This is the same as pressing the backbutton on Android device. + */ + public void backHistory() { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + webView.backHistory(); + } + }); + } + + /** + * Override the default behavior of the Android back button. + * If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired. + * + * @param override T=override, F=cancel override + */ + public void overrideBackbutton(boolean override) { + LOG.i("App", "WARNING: Back Button Default Behavior will be overridden. The backbutton event will be fired!"); + webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_BACK, override); + } + + /** + * Override the default behavior of the Android volume buttons. + * If overridden, when the volume button is pressed, the "volume[up|down]button" JavaScript event will be fired. + * + * @param button volumeup, volumedown + * @param override T=override, F=cancel override + */ + public void overrideButton(String button, boolean override) { + LOG.i("App", "WARNING: Volume Button Default Behavior will be overridden. The volume event will be fired!"); + if (button.equals("volumeup")) { + webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_UP, override); + } + else if (button.equals("volumedown")) { + webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_DOWN, override); + } + else if (button.equals("menubutton")) { + webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_MENU, override); + } + } + + /** + * Return whether the Android back button is overridden by the user. + * + * @return boolean + */ + public boolean isBackbuttonOverridden() { + return webView.isButtonPlumbedToJs(KeyEvent.KEYCODE_BACK); + } + + /** + * Exit the Android application. + */ + public void exitApp() { + this.webView.getPluginManager().postMessage("exit", null); + } + + + /** + * Listen for telephony events: RINGING, OFFHOOK and IDLE + * Send these events to all plugins using + * CordovaActivity.onMessage("telephone", "ringing" | "offhook" | "idle") + */ + private void initTelephonyReceiver() { + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); + //final CordovaInterface mycordova = this.cordova; + this.telephonyReceiver = new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + + // If state has changed + if ((intent != null) && intent.getAction().equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { + if (intent.hasExtra(TelephonyManager.EXTRA_STATE)) { + String extraData = intent.getStringExtra(TelephonyManager.EXTRA_STATE); + if (extraData.equals(TelephonyManager.EXTRA_STATE_RINGING)) { + LOG.i(TAG, "Telephone RINGING"); + webView.getPluginManager().postMessage("telephone", "ringing"); + } + else if (extraData.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) { + LOG.i(TAG, "Telephone OFFHOOK"); + webView.getPluginManager().postMessage("telephone", "offhook"); + } + else if (extraData.equals(TelephonyManager.EXTRA_STATE_IDLE)) { + LOG.i(TAG, "Telephone IDLE"); + webView.getPluginManager().postMessage("telephone", "idle"); + } + } + } + } + }; + + // Register the receiver + webView.getContext().registerReceiver(this.telephonyReceiver, intentFilter); + } + + private void sendEventMessage(String action) { + JSONObject obj = new JSONObject(); + try { + obj.put("action", action); + } catch (JSONException e) { + LOG.e(TAG, "Failed to create event message", e); + } + PluginResult result = new PluginResult(PluginResult.Status.OK, obj); + + if (messageChannel == null) { + LOG.i(TAG, "Request to send event before messageChannel initialised: " + action); + if ("pause".equals(action)) { + pendingPause = result; + } else if ("resume".equals(action)) { + // When starting normally onPause then onResume is called + pendingPause = null; + } + } else { + sendEventMessage(result); + } + } + + private void sendEventMessage(PluginResult payload) { + payload.setKeepCallback(true); + if (messageChannel != null) { + messageChannel.sendPluginResult(payload); + } + } + + /* + * Unregister the receiver + * + */ + public void onDestroy() + { + webView.getContext().unregisterReceiver(this.telephonyReceiver); + } + + /** + * Used to send the resume event in the case that the Activity is destroyed by the OS + * + * @param resumeEvent PluginResult containing the payload for the resume event to be fired + */ + public void sendResumeEvent(PluginResult resumeEvent) { + // This operation must be synchronized because plugin results that trigger resume + // events can be processed asynchronously + synchronized(messageChannelLock) { + if (messageChannel != null) { + sendEventMessage(resumeEvent); + } else { + // Might get called before the page loads, so we need to store it until the + // messageChannel gets created + this.pendingResume = resumeEvent; + } + } + } + + /* + * This needs to be implemented if you wish to use the Camera Plugin or other plugins + * that read the Build Configuration. + * + * Thanks to Phil@Medtronic and Graham Borland for finding the answer and posting it to + * StackOverflow. This is annoying as hell! + * + * @deprecated Use {@link BuildHelper#getBuildConfigValue} instead. + */ + @Deprecated + public static Object getBuildConfigValue(Context ctx, String key) + { + LOG.w(TAG, "CoreAndroid.getBuildConfigValue is deprecated and will be removed in a future release. Use BuildHelper.getBuildConfigValue instead."); + return BuildHelper.getBuildConfigValue(ctx, key); + } +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/ExposedJsApi.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/ExposedJsApi.java new file mode 100644 index 00000000..acc65c6f --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/ExposedJsApi.java @@ -0,0 +1,31 @@ +/* + 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 org.apache.cordova; + +import org.json.JSONException; + +/* + * Any exposed Javascript API MUST implement these three things! + */ +public interface ExposedJsApi { + public String exec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException; + public void setNativeToJsBridgeMode(int bridgeSecret, int value) throws IllegalAccessException; + public String retrieveJsMessages(int bridgeSecret, boolean fromOnlineEvent) throws IllegalAccessException; +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/ICordovaClientCertRequest.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/ICordovaClientCertRequest.java new file mode 100644 index 00000000..1417a156 --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/ICordovaClientCertRequest.java @@ -0,0 +1,66 @@ +/* + 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 org.apache.cordova; + +import java.security.Principal; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +/** + * Specifies interface for handling certificate requests. + */ +public interface ICordovaClientCertRequest { + /** + * Cancel this request + */ + public void cancel(); + + /* + * Returns the host name of the server requesting the certificate. + */ + public String getHost(); + + /* + * Returns the acceptable types of asymmetric keys (can be null). + */ + public String[] getKeyTypes(); + + /* + * Returns the port number of the server requesting the certificate. + */ + public int getPort(); + + /* + * Returns the acceptable certificate issuers for the certificate matching the private key (can be null). + */ + public Principal[] getPrincipals(); + + /* + * Ignore the request for now. Do not remember user's choice. + */ + public void ignore(); + + /* + * Proceed with the specified private key and client certificate chain. Remember the user's positive choice and use it for future requests. + * + * @param privateKey The privateKey + * @param chain The certificate chain + */ + public void proceed(PrivateKey privateKey, X509Certificate[] chain); +} \ No newline at end of file diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/ICordovaCookieManager.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/ICordovaCookieManager.java new file mode 100644 index 00000000..e776194f --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/ICordovaCookieManager.java @@ -0,0 +1,33 @@ +/* + 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 org.apache.cordova; + +public interface ICordovaCookieManager { + + public void setCookiesEnabled(boolean accept); + + public void setCookie(final String url, final String value); + + public String getCookie(final String url); + + public void clearCookies(); + + public void flush(); +}; diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/ICordovaHttpAuthHandler.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/ICordovaHttpAuthHandler.java new file mode 100644 index 00000000..c89e5b9a --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/ICordovaHttpAuthHandler.java @@ -0,0 +1,38 @@ +/* + 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 org.apache.cordova; + +/** + * Specifies interface for HTTP auth handler object which is used to handle auth requests and + * specifying user credentials. + */ + public interface ICordovaHttpAuthHandler { + /** + * Instructs the WebView to cancel the authentication request. + */ + public void cancel (); + + /** + * Instructs the WebView to proceed with the authentication with the given credentials. + * + * @param username The user name + * @param password The password + */ + public void proceed (String username, String password); +} \ No newline at end of file diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/LOG.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/LOG.java new file mode 100755 index 00000000..9fe7a7df --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/LOG.java @@ -0,0 +1,244 @@ +/* + 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 org.apache.cordova; + +import android.util.Log; + +/** + * Log to Android logging system. + * + * Log message can be a string or a printf formatted string with arguments. + * See http://developer.android.com/reference/java/util/Formatter.html + */ +public class LOG { + + public static final int VERBOSE = Log.VERBOSE; + public static final int DEBUG = Log.DEBUG; + public static final int INFO = Log.INFO; + public static final int WARN = Log.WARN; + public static final int ERROR = Log.ERROR; + + // Current log level + public static int LOGLEVEL = Log.ERROR; + + /** + * Set the current log level. + * + * @param logLevel + */ + public static void setLogLevel(int logLevel) { + LOGLEVEL = logLevel; + Log.i("CordovaLog", "Changing log level to " + logLevel); + } + + /** + * Set the current log level. + * + * @param logLevel + */ + public static void setLogLevel(String logLevel) { + if ("VERBOSE".equals(logLevel)) LOGLEVEL = VERBOSE; + else if ("DEBUG".equals(logLevel)) LOGLEVEL = DEBUG; + else if ("INFO".equals(logLevel)) LOGLEVEL = INFO; + else if ("WARN".equals(logLevel)) LOGLEVEL = WARN; + else if ("ERROR".equals(logLevel)) LOGLEVEL = ERROR; + Log.i("CordovaLog", "Changing log level to " + logLevel + "(" + LOGLEVEL + ")"); + } + + /** + * Determine if log level will be logged + * + * @param logLevel + * @return true if the parameter passed in is greater than or equal to the current log level + */ + public static boolean isLoggable(int logLevel) { + return (logLevel >= LOGLEVEL); + } + + /** + * Verbose log message. + * + * @param tag + * @param s + */ + public static void v(String tag, String s) { + if (LOG.VERBOSE >= LOGLEVEL) Log.v(tag, s); + } + + /** + * Debug log message. + * + * @param tag + * @param s + */ + public static void d(String tag, String s) { + if (LOG.DEBUG >= LOGLEVEL) Log.d(tag, s); + } + + /** + * Info log message. + * + * @param tag + * @param s + */ + public static void i(String tag, String s) { + if (LOG.INFO >= LOGLEVEL) Log.i(tag, s); + } + + /** + * Warning log message. + * + * @param tag + * @param s + */ + public static void w(String tag, String s) { + if (LOG.WARN >= LOGLEVEL) Log.w(tag, s); + } + + /** + * Error log message. + * + * @param tag + * @param s + */ + public static void e(String tag, String s) { + if (LOG.ERROR >= LOGLEVEL) Log.e(tag, s); + } + + /** + * Verbose log message. + * + * @param tag + * @param s + * @param e + */ + public static void v(String tag, String s, Throwable e) { + if (LOG.VERBOSE >= LOGLEVEL) Log.v(tag, s, e); + } + + /** + * Debug log message. + * + * @param tag + * @param s + * @param e + */ + public static void d(String tag, String s, Throwable e) { + if (LOG.DEBUG >= LOGLEVEL) Log.d(tag, s, e); + } + + /** + * Info log message. + * + * @param tag + * @param s + * @param e + */ + public static void i(String tag, String s, Throwable e) { + if (LOG.INFO >= LOGLEVEL) Log.i(tag, s, e); + } + + /** + * Warning log message. + * + * @param tag + * @param e + */ + public static void w(String tag, Throwable e) { + if (LOG.WARN >= LOGLEVEL) Log.w(tag, e); + } + + /** + * Warning log message. + * + * @param tag + * @param s + * @param e + */ + public static void w(String tag, String s, Throwable e) { + if (LOG.WARN >= LOGLEVEL) Log.w(tag, s, e); + } + + /** + * Error log message. + * + * @param tag + * @param s + * @param e + */ + public static void e(String tag, String s, Throwable e) { + if (LOG.ERROR >= LOGLEVEL) Log.e(tag, s, e); + } + + /** + * Verbose log message with printf formatting. + * + * @param tag + * @param s + * @param args + */ + public static void v(String tag, String s, Object... args) { + if (LOG.VERBOSE >= LOGLEVEL) Log.v(tag, String.format(s, args)); + } + + /** + * Debug log message with printf formatting. + * + * @param tag + * @param s + * @param args + */ + public static void d(String tag, String s, Object... args) { + if (LOG.DEBUG >= LOGLEVEL) Log.d(tag, String.format(s, args)); + } + + /** + * Info log message with printf formatting. + * + * @param tag + * @param s + * @param args + */ + public static void i(String tag, String s, Object... args) { + if (LOG.INFO >= LOGLEVEL) Log.i(tag, String.format(s, args)); + } + + /** + * Warning log message with printf formatting. + * + * @param tag + * @param s + * @param args + */ + public static void w(String tag, String s, Object... args) { + if (LOG.WARN >= LOGLEVEL) Log.w(tag, String.format(s, args)); + } + + /** + * Error log message with printf formatting. + * + * @param tag + * @param s + * @param args + */ + public static void e(String tag, String s, Object... args) { + if (LOG.ERROR >= LOGLEVEL) Log.e(tag, String.format(s, args)); + } + +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/NativeToJsMessageQueue.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/NativeToJsMessageQueue.java new file mode 100755 index 00000000..311ba444 --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/NativeToJsMessageQueue.java @@ -0,0 +1,552 @@ +/* + 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 org.apache.cordova; + +import java.util.ArrayList; +import java.util.LinkedList; + +/** + * Holds the list of messages to be sent to the WebView. + */ +public class NativeToJsMessageQueue { + private static final String LOG_TAG = "JsMessageQueue"; + + // Set this to true to force plugin results to be encoding as + // JS instead of the custom format (useful for benchmarking). + // Doesn't work for multipart messages. + private static final boolean FORCE_ENCODE_USING_EVAL = false; + + // Disable sending back native->JS messages during an exec() when the active + // exec() is asynchronous. Set this to true when running bridge benchmarks. + static final boolean DISABLE_EXEC_CHAINING = false; + + // A hopefully reasonable upper limit of how much combined payload data + // to send to the JavaScript in one shot. + // This currently only chops up on message boundaries. + // It may be useful to split and reassemble response messages someday. + private static int COMBINED_RESPONSE_CUTOFF = 16 * 1024 * 1024; + + /** + * When true, the active listener is not fired upon enqueue. When set to false, + * the active listener will be fired if the queue is non-empty. + */ + private boolean paused; + + /** + * The list of JavaScript statements to be sent to JavaScript. + */ + private final LinkedList queue = new LinkedList(); + + /** + * The array of listeners that can be used to send messages to JS. + */ + private ArrayList bridgeModes = new ArrayList(); + + /** + * When null, the bridge is disabled. This occurs during page transitions. + * When disabled, all callbacks are dropped since they are assumed to be + * relevant to the previous page. + */ + private BridgeMode activeBridgeMode; + + public void addBridgeMode(BridgeMode bridgeMode) { + bridgeModes.add(bridgeMode); + } + + public boolean isBridgeEnabled() { + return activeBridgeMode != null; + } + + public boolean isEmpty() { + return queue.isEmpty(); + } + + /** + * Changes the bridge mode. + */ + public void setBridgeMode(int value) { + if (value < -1 || value >= bridgeModes.size()) { + LOG.d(LOG_TAG, "Invalid NativeToJsBridgeMode: " + value); + } else { + BridgeMode newMode = value < 0 ? null : bridgeModes.get(value); + if (newMode != activeBridgeMode) { + LOG.d(LOG_TAG, "Set native->JS mode to " + (newMode == null ? "null" : newMode.getClass().getSimpleName())); + synchronized (this) { + activeBridgeMode = newMode; + if (newMode != null) { + newMode.reset(); + if (!paused && !queue.isEmpty()) { + newMode.onNativeToJsMessageAvailable(this); + } + } + } + } + } + } + + /** + * Clears all messages and resets to the default bridge mode. + */ + public void reset() { + synchronized (this) { + queue.clear(); + setBridgeMode(-1); + } + } + + private int calculatePackedMessageLength(JsMessage message) { + int messageLen = message.calculateEncodedLength(); + String messageLenStr = String.valueOf(messageLen); + return messageLenStr.length() + messageLen + 1; + } + + private void packMessage(JsMessage message, StringBuilder sb) { + int len = message.calculateEncodedLength(); + sb.append(len) + .append(' '); + message.encodeAsMessage(sb); + } + + /** + * Combines and returns queued messages combined into a single string. + * + * Combines as many messages as possible, without exceeding + * COMBINED_RESPONSE_CUTOFF in case of multiple response messages. + * + * Returns null if the queue is empty. + */ + public String popAndEncode(boolean fromOnlineEvent) { + synchronized (this) { + if (activeBridgeMode == null) { + return null; + } + activeBridgeMode.notifyOfFlush(this, fromOnlineEvent); + if (queue.isEmpty()) { + return null; + } + int totalPayloadLen = 0; + int numMessagesToSend = 0; + for (JsMessage message : queue) { + int messageSize = calculatePackedMessageLength(message); + if (numMessagesToSend > 0 && + COMBINED_RESPONSE_CUTOFF > 0 && + totalPayloadLen + messageSize > COMBINED_RESPONSE_CUTOFF + ) { + break; + } + totalPayloadLen += messageSize; + numMessagesToSend += 1; + } + + StringBuilder sb = new StringBuilder(totalPayloadLen); + for (int i = 0; i < numMessagesToSend; ++i) { + JsMessage message = queue.removeFirst(); + packMessage(message, sb); + } + + if (!queue.isEmpty()) { + // Attach a char to indicate that there are more messages pending. + sb.append('*'); + } + String ret = sb.toString(); + return ret; + } + } + + /** + * Same as popAndEncode(), except encodes in a form that can be executed as JS. + */ + public String popAndEncodeAsJs() { + synchronized (this) { + int length = queue.size(); + if (length == 0) { + return null; + } + int totalPayloadLen = 0; + int numMessagesToSend = 0; + for (JsMessage message : queue) { + int messageSize = message.calculateEncodedLength() + 50; // overestimate. + if (numMessagesToSend > 0 && + COMBINED_RESPONSE_CUTOFF > 0 && + totalPayloadLen + messageSize > COMBINED_RESPONSE_CUTOFF + ) { + break; + } + totalPayloadLen += messageSize; + numMessagesToSend += 1; + } + boolean willSendAllMessages = numMessagesToSend == queue.size(); + StringBuilder sb = new StringBuilder(totalPayloadLen + (willSendAllMessages ? 0 : 100)); + // Wrap each statement in a try/finally so that if one throws it does + // not affect the next. + for (int i = 0; i < numMessagesToSend; ++i) { + JsMessage message = queue.removeFirst(); + if (willSendAllMessages && (i + 1 == numMessagesToSend)) { + message.encodeAsJsMessage(sb); + } else { + sb.append("try{"); + message.encodeAsJsMessage(sb); + sb.append("}finally{"); + } + } + if (!willSendAllMessages) { + sb.append("window.setTimeout(function(){cordova.require('cordova/plugin/android/polling').pollOnce();},0);"); + } + for (int i = willSendAllMessages ? 1 : 0; i < numMessagesToSend; ++i) { + sb.append('}'); + } + String ret = sb.toString(); + return ret; + } + } + + /** + * Add a JavaScript statement to the list. + */ + public void addJavaScript(String statement) { + enqueueMessage(new JsMessage(statement)); + } + + /** + * Add a JavaScript statement to the list. + */ + public void addPluginResult(PluginResult result, String callbackId) { + if (callbackId == null) { + LOG.e(LOG_TAG, "Got plugin result with no callbackId", new Throwable()); + return; + } + // Don't send anything if there is no result and there is no need to + // clear the callbacks. + boolean noResult = result.getStatus() == PluginResult.Status.NO_RESULT.ordinal(); + boolean keepCallback = result.getKeepCallback(); + if (noResult && keepCallback) { + return; + } + JsMessage message = new JsMessage(result, callbackId); + if (FORCE_ENCODE_USING_EVAL) { + StringBuilder sb = new StringBuilder(message.calculateEncodedLength() + 50); + message.encodeAsJsMessage(sb); + message = new JsMessage(sb.toString()); + } + + enqueueMessage(message); + } + + private void enqueueMessage(JsMessage message) { + synchronized (this) { + if (activeBridgeMode == null) { + LOG.d(LOG_TAG, "Dropping Native->JS message due to disabled bridge"); + return; + } + queue.add(message); + if (!paused) { + activeBridgeMode.onNativeToJsMessageAvailable(this); + } + } + } + + public void setPaused(boolean value) { + if (paused && value) { + // This should never happen. If a use-case for it comes up, we should + // change pause to be a counter. + LOG.e(LOG_TAG, "nested call to setPaused detected.", new Throwable()); + } + paused = value; + if (!value) { + synchronized (this) { + if (!queue.isEmpty() && activeBridgeMode != null) { + activeBridgeMode.onNativeToJsMessageAvailable(this); + } + } + } + } + + public static abstract class BridgeMode { + public abstract void onNativeToJsMessageAvailable(NativeToJsMessageQueue queue); + public void notifyOfFlush(NativeToJsMessageQueue queue, boolean fromOnlineEvent) {} + public void reset() {} + } + + /** Uses JS polls for messages on a timer.. */ + public static class NoOpBridgeMode extends BridgeMode { + @Override public void onNativeToJsMessageAvailable(NativeToJsMessageQueue queue) { + } + } + + /** Uses webView.loadUrl("javascript:") to execute messages. */ + public static class LoadUrlBridgeMode extends BridgeMode { + private final CordovaWebViewEngine engine; + private final CordovaInterface cordova; + + public LoadUrlBridgeMode(CordovaWebViewEngine engine, CordovaInterface cordova) { + this.engine = engine; + this.cordova = cordova; + } + + @Override + public void onNativeToJsMessageAvailable(final NativeToJsMessageQueue queue) { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + String js = queue.popAndEncodeAsJs(); + if (js != null) { + engine.loadUrl("javascript:" + js, false); + } + } + }); + } + } + + /** Uses online/offline events to tell the JS when to poll for messages. */ + public static class OnlineEventsBridgeMode extends BridgeMode { + private final OnlineEventsBridgeModeDelegate delegate; + private boolean online; + private boolean ignoreNextFlush; + + public interface OnlineEventsBridgeModeDelegate { + void setNetworkAvailable(boolean value); + void runOnUiThread(Runnable r); + } + + public OnlineEventsBridgeMode(OnlineEventsBridgeModeDelegate delegate) { + this.delegate = delegate; + } + + @Override + public void reset() { + delegate.runOnUiThread(new Runnable() { + public void run() { + online = false; + // If the following call triggers a notifyOfFlush, then ignore it. + ignoreNextFlush = true; + delegate.setNetworkAvailable(true); + } + }); + } + + @Override + public void onNativeToJsMessageAvailable(final NativeToJsMessageQueue queue) { + delegate.runOnUiThread(new Runnable() { + public void run() { + if (!queue.isEmpty()) { + ignoreNextFlush = false; + delegate.setNetworkAvailable(online); + } + } + }); + } + // Track when online/offline events are fired so that we don't fire excess events. + @Override + public void notifyOfFlush(final NativeToJsMessageQueue queue, boolean fromOnlineEvent) { + if (fromOnlineEvent && !ignoreNextFlush) { + online = !online; + } + } + } + + /** Uses webView.evaluateJavascript to execute messages. */ + public static class EvalBridgeMode extends BridgeMode { + private final CordovaWebViewEngine engine; + private final CordovaInterface cordova; + + public EvalBridgeMode(CordovaWebViewEngine engine, CordovaInterface cordova) { + this.engine = engine; + this.cordova = cordova; + } + + @Override + public void onNativeToJsMessageAvailable(final NativeToJsMessageQueue queue) { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + String js = queue.popAndEncodeAsJs(); + if (js != null) { + engine.evaluateJavascript(js, null); + } + } + }); + } + } + + + + private static class JsMessage { + final String jsPayloadOrCallbackId; + final PluginResult pluginResult; + JsMessage(String js) { + if (js == null) { + throw new NullPointerException(); + } + jsPayloadOrCallbackId = js; + pluginResult = null; + } + JsMessage(PluginResult pluginResult, String callbackId) { + if (callbackId == null || pluginResult == null) { + throw new NullPointerException(); + } + jsPayloadOrCallbackId = callbackId; + this.pluginResult = pluginResult; + } + + static int calculateEncodedLengthHelper(PluginResult pluginResult) { + switch (pluginResult.getMessageType()) { + case PluginResult.MESSAGE_TYPE_BOOLEAN: // f or t + case PluginResult.MESSAGE_TYPE_NULL: // N + return 1; + case PluginResult.MESSAGE_TYPE_NUMBER: // n + return 1 + pluginResult.getMessage().length(); + case PluginResult.MESSAGE_TYPE_STRING: // s + return 1 + pluginResult.getStrMessage().length(); + case PluginResult.MESSAGE_TYPE_BINARYSTRING: + return 1 + pluginResult.getMessage().length(); + case PluginResult.MESSAGE_TYPE_ARRAYBUFFER: + return 1 + pluginResult.getMessage().length(); + case PluginResult.MESSAGE_TYPE_MULTIPART: + int ret = 1; + for (int i = 0; i < pluginResult.getMultipartMessagesSize(); i++) { + int length = calculateEncodedLengthHelper(pluginResult.getMultipartMessage(i)); + int argLength = String.valueOf(length).length(); + ret += argLength + 1 + length; + } + return ret; + case PluginResult.MESSAGE_TYPE_JSON: + default: + return pluginResult.getMessage().length(); + } + } + + int calculateEncodedLength() { + if (pluginResult == null) { + return jsPayloadOrCallbackId.length() + 1; + } + int statusLen = String.valueOf(pluginResult.getStatus()).length(); + int ret = 2 + statusLen + 1 + jsPayloadOrCallbackId.length() + 1; + return ret + calculateEncodedLengthHelper(pluginResult); + } + + static void encodeAsMessageHelper(StringBuilder sb, PluginResult pluginResult) { + switch (pluginResult.getMessageType()) { + case PluginResult.MESSAGE_TYPE_BOOLEAN: + sb.append(pluginResult.getMessage().charAt(0)); // t or f. + break; + case PluginResult.MESSAGE_TYPE_NULL: // N + sb.append('N'); + break; + case PluginResult.MESSAGE_TYPE_NUMBER: // n + sb.append('n') + .append(pluginResult.getMessage()); + break; + case PluginResult.MESSAGE_TYPE_STRING: // s + sb.append('s'); + sb.append(pluginResult.getStrMessage()); + break; + case PluginResult.MESSAGE_TYPE_BINARYSTRING: // S + sb.append('S'); + sb.append(pluginResult.getMessage()); + break; + case PluginResult.MESSAGE_TYPE_ARRAYBUFFER: // A + sb.append('A'); + sb.append(pluginResult.getMessage()); + break; + case PluginResult.MESSAGE_TYPE_MULTIPART: + sb.append('M'); + for (int i = 0; i < pluginResult.getMultipartMessagesSize(); i++) { + PluginResult multipartMessage = pluginResult.getMultipartMessage(i); + sb.append(String.valueOf(calculateEncodedLengthHelper(multipartMessage))); + sb.append(' '); + encodeAsMessageHelper(sb, multipartMessage); + } + break; + case PluginResult.MESSAGE_TYPE_JSON: + default: + sb.append(pluginResult.getMessage()); // [ or { + } + } + + void encodeAsMessage(StringBuilder sb) { + if (pluginResult == null) { + sb.append('J') + .append(jsPayloadOrCallbackId); + return; + } + int status = pluginResult.getStatus(); + boolean noResult = status == PluginResult.Status.NO_RESULT.ordinal(); + boolean resultOk = status == PluginResult.Status.OK.ordinal(); + boolean keepCallback = pluginResult.getKeepCallback(); + + sb.append((noResult || resultOk) ? 'S' : 'F') + .append(keepCallback ? '1' : '0') + .append(status) + .append(' ') + .append(jsPayloadOrCallbackId) + .append(' '); + + encodeAsMessageHelper(sb, pluginResult); + } + + void buildJsMessage(StringBuilder sb) { + switch (pluginResult.getMessageType()) { + case PluginResult.MESSAGE_TYPE_MULTIPART: + int size = pluginResult.getMultipartMessagesSize(); + for (int i=0; i pluginMap = Collections.synchronizedMap(new LinkedHashMap()); + private final Map entryMap = Collections.synchronizedMap(new LinkedHashMap()); + + private final CordovaInterface ctx; + private final CordovaWebView app; + private boolean isInitialized; + + private CordovaPlugin permissionRequester; + + public PluginManager(CordovaWebView cordovaWebView, CordovaInterface cordova, Collection pluginEntries) { + this.ctx = cordova; + this.app = cordovaWebView; + setPluginEntries(pluginEntries); + } + + public Collection getPluginEntries() { + return entryMap.values(); + } + + public void setPluginEntries(Collection pluginEntries) { + if (isInitialized) { + this.onPause(false); + this.onDestroy(); + pluginMap.clear(); + entryMap.clear(); + } + for (PluginEntry entry : pluginEntries) { + addService(entry); + } + if (isInitialized) { + startupPlugins(); + } + } + + /** + * Init when loading a new HTML page into webview. + */ + public void init() { + LOG.d(TAG, "init()"); + isInitialized = true; + this.onPause(false); + this.onDestroy(); + pluginMap.clear(); + this.startupPlugins(); + } + + /** + * Create plugins objects that have onload set. + */ + private void startupPlugins() { + synchronized (entryMap) { + for (PluginEntry entry : entryMap.values()) { + // Add a null entry to for each non-startup plugin to avoid ConcurrentModificationException + // When iterating plugins. + if (entry.onload) { + getPlugin(entry.service); + } + else { + LOG.d(TAG, "startupPlugins: put - " + entry.service); + pluginMap.put(entry.service, null); + } + } + } + } + + /** + * Receives a request for execution and fulfills it by finding the appropriate + * Java class and calling it's execute method. + * + * PluginManager.exec can be used either synchronously or async. In either case, a JSON encoded + * string is returned that will indicate if any errors have occurred when trying to find + * or execute the class denoted by the clazz argument. + * + * @param service String containing the service to run + * @param action String containing the action that the class is supposed to perform. This is + * passed to the plugin execute method and it is up to the plugin developer + * how to deal with it. + * @param callbackId String containing the id of the callback that is execute in JavaScript if + * this is an async plugin call. + * @param rawArgs An Array literal string containing any arguments needed in the + * plugin execute method. + */ + public void exec(final String service, final String action, final String callbackId, final String rawArgs) { + CordovaPlugin plugin = getPlugin(service); + if (plugin == null) { + LOG.d(TAG, "exec() call to unknown plugin: " + service); + PluginResult cr = new PluginResult(PluginResult.Status.CLASS_NOT_FOUND_EXCEPTION); + app.sendPluginResult(cr, callbackId); + return; + } + CallbackContext callbackContext = new CallbackContext(callbackId, app); + try { + long pluginStartTime = System.currentTimeMillis(); + boolean wasValidAction = plugin.execute(action, rawArgs, callbackContext); + long duration = System.currentTimeMillis() - pluginStartTime; + + if (duration > SLOW_EXEC_WARNING_THRESHOLD) { + LOG.w(TAG, "THREAD WARNING: exec() call to " + service + "." + action + " blocked the main thread for " + duration + "ms. Plugin should use CordovaInterface.getThreadPool()."); + } + if (!wasValidAction) { + PluginResult cr = new PluginResult(PluginResult.Status.INVALID_ACTION); + callbackContext.sendPluginResult(cr); + } + } catch (JSONException e) { + PluginResult cr = new PluginResult(PluginResult.Status.JSON_EXCEPTION); + callbackContext.sendPluginResult(cr); + } catch (Exception e) { + LOG.e(TAG, "Uncaught exception from plugin", e); + callbackContext.error(e.getMessage()); + } + } + + /** + * Get the plugin object that implements the service. + * If the plugin object does not already exist, then create it. + * If the service doesn't exist, then return null. + * + * @param service The name of the service. + * @return CordovaPlugin or null + */ + public CordovaPlugin getPlugin(String service) { + CordovaPlugin ret = pluginMap.get(service); + if (ret == null) { + PluginEntry pe = entryMap.get(service); + if (pe == null) { + return null; + } + if (pe.plugin != null) { + ret = pe.plugin; + } else { + ret = instantiatePlugin(pe.pluginClass); + } + ret.privateInitialize(service, ctx, app, app.getPreferences()); + LOG.d(TAG, "getPlugin - put: " + service); + pluginMap.put(service, ret); + } + return ret; + } + + /** + * Add a plugin class that implements a service to the service entry table. + * This does not create the plugin object instance. + * + * @param service The service name + * @param className The plugin class name + */ + public void addService(String service, String className) { + addService(service, className, false); + } + + /** + * Add a plugin class that implements a service to the service entry table. + * + * @param service The service name + * @param className The plugin class name + * @param onload If true, the plugin will be instantiated immediately + */ + public void addService(String service, String className, boolean onload) { + PluginEntry entry = new PluginEntry(service, className, onload); + this.addService(entry); + } + + /** + * Add a plugin class that implements a service to the service entry table. + * This does not create the plugin object instance. + * + * @param entry The plugin entry + */ + public void addService(PluginEntry entry) { + this.entryMap.put(entry.service, entry); + if (entry.plugin != null) { + entry.plugin.privateInitialize(entry.service, ctx, app, app.getPreferences()); + LOG.d(TAG, "addService: put - " + entry.service); + pluginMap.put(entry.service, entry.plugin); + } + } + + /** + * Called when the system is about to start resuming a previous activity. + * + * @param multitasking Flag indicating if multitasking is turned on for app + */ + public void onPause(boolean multitasking) { + synchronized (this.pluginMap) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + plugin.onPause(multitasking); + } + } + } + } + + /** + * Called when the system received an HTTP authentication request. Plugins can use + * the supplied HttpAuthHandler to process this auth challenge. + * + * @param view The WebView that is initiating the callback + * @param handler The HttpAuthHandler used to set the WebView's response + * @param host The host requiring authentication + * @param realm The realm for which authentication is required + * + * @return Returns True if there is a plugin which will resolve this auth challenge, otherwise False + * + */ + public boolean onReceivedHttpAuthRequest(CordovaWebView view, ICordovaHttpAuthHandler handler, String host, String realm) { + synchronized (this.pluginMap) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null && plugin.onReceivedHttpAuthRequest(app, handler, host, realm)) { + return true; + } + } + } + return false; + } + + /** + * Called when he system received an SSL client certificate request. Plugin can use + * the supplied ClientCertRequest to process this certificate challenge. + * + * @param view The WebView that is initiating the callback + * @param request The client certificate request + * + * @return Returns True if plugin will resolve this auth challenge, otherwise False + * + */ + public boolean onReceivedClientCertRequest(CordovaWebView view, ICordovaClientCertRequest request) { + synchronized (this.pluginMap) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null && plugin.onReceivedClientCertRequest(app, request)) { + return true; + } + } + } + return false; + } + + /** + * Called when the activity will start interacting with the user. + * + * @param multitasking Flag indicating if multitasking is turned on for app + */ + public void onResume(boolean multitasking) { + synchronized (this.pluginMap) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + plugin.onResume(multitasking); + } + } + } + } + + /** + * Called when the activity is becoming visible to the user. + */ + public void onStart() { + synchronized (this.pluginMap) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + plugin.onStart(); + } + } + } + } + + /** + * Called when the activity is no longer visible to the user. + */ + public void onStop() { + synchronized (this.pluginMap) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + plugin.onStop(); + } + } + } + } + + /** + * The final call you receive before your activity is destroyed. + */ + public void onDestroy() { + synchronized (this.pluginMap) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + plugin.onDestroy(); + } + } + } + } + + /** + * Send a message to all plugins. + * + * @param id The message id + * @param data The message data + * @return Object to stop propagation or null + */ + public Object postMessage(String id, Object data) { + LOG.d(TAG, "postMessage: " + id); + synchronized (this.pluginMap) { + this.pluginMap.forEach((s, plugin) -> { + if (plugin != null) { + plugin.onMessage(id, data); + } + }); + } + return ctx.onMessage(id, data); + } + + /** + * Called when the activity receives a new intent. + */ + public void onNewIntent(Intent intent) { + synchronized (this.pluginMap) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + plugin.onNewIntent(intent); + } + } + } + } + + /** + * @todo should we move this somewhere public and accessible by all plugins? + * For now, it is placed where it is used and kept private so we can decide later and move without causing a breaking change. + * An ideal location might be in the "ConfigXmlParser" at the time it generates the "launchUrl". + * + * @todo should we be restrictive on the "file://" return? e.g. "file:///android_asset/www/" + * Would be considered as a breaking change if we apply a more granular check. + */ + private String getLaunchUrlPrefix() { + if (!app.getPreferences().getBoolean("AndroidInsecureFileModeEnabled", false)) { + String scheme = app.getPreferences().getString("scheme", SCHEME_HTTPS).toLowerCase(); + String hostname = app.getPreferences().getString("hostname", DEFAULT_HOSTNAME).toLowerCase(); + return scheme + "://" + hostname + '/'; + } + + return "file://"; + } + + /** + * Called when the webview is going to request an external resource. + * + * This delegates to the installed plugins, and returns true/false for the + * first plugin to provide a non-null result. If no plugins respond, then + * the default policy is applied. + * + * @param url The URL that is being requested. + * @return Returns true to allow the resource to load, + * false to block the resource. + */ + public boolean shouldAllowRequest(String url) { + synchronized (this.entryMap) { + for (PluginEntry entry : this.entryMap.values()) { + CordovaPlugin plugin = pluginMap.get(entry.service); + if (plugin != null) { + Boolean result = plugin.shouldAllowRequest(url); + if (result != null) { + return result; + } + } + } + } + + // Default policy: + if (url.startsWith("blob:") || url.startsWith("data:") || url.startsWith("about:blank")) { + return true; + } + // TalkBack requires this, so allow it by default. + if (url.startsWith("https://ssl.gstatic.com/accessibility/javascript/android/")) { + return true; + } + if (url.startsWith("file://")) { + //This directory on WebKit/Blink based webviews contains SQLite databases! + //DON'T CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING! + return !url.contains("/app_webview/"); + } + return false; + } + + /** + * Called when the webview is going to change the URL of the loaded content. + * + * This delegates to the installed plugins, and returns true/false for the + * first plugin to provide a non-null result. If no plugins respond, then + * the default policy is applied. + * + * @param url The URL that is being requested. + * @return Returns true to allow the navigation, + * false to block the navigation. + */ + public boolean shouldAllowNavigation(String url) { + synchronized (this.entryMap) { + for (PluginEntry entry : this.entryMap.values()) { + CordovaPlugin plugin = pluginMap.get(entry.service); + if (plugin != null) { + Boolean result = plugin.shouldAllowNavigation(url); + if (result != null) { + return result; + } + } + } + } + + // Default policy: + return url.startsWith(getLaunchUrlPrefix()) || url.startsWith("about:blank"); + } + + + /** + * Called when the webview is requesting the exec() bridge be enabled. + */ + public boolean shouldAllowBridgeAccess(String url) { + synchronized (this.entryMap) { + for (PluginEntry entry : this.entryMap.values()) { + CordovaPlugin plugin = pluginMap.get(entry.service); + if (plugin != null) { + Boolean result = plugin.shouldAllowBridgeAccess(url); + if (result != null) { + return result; + } + } + } + } + + // Default policy: + return url.startsWith(getLaunchUrlPrefix()); + } + + /** + * Called when the webview is going not going to navigate, but may launch + * an Intent for an URL. + * + * This delegates to the installed plugins, and returns true/false for the + * first plugin to provide a non-null result. If no plugins respond, then + * the default policy is applied. + * + * @param url The URL that is being requested. + * @return Returns true to allow the URL to launch an intent, + * false to block the intent. + */ + public Boolean shouldOpenExternalUrl(String url) { + synchronized (this.entryMap) { + for (PluginEntry entry : this.entryMap.values()) { + CordovaPlugin plugin = pluginMap.get(entry.service); + if (plugin != null) { + Boolean result = plugin.shouldOpenExternalUrl(url); + if (result != null) { + return result; + } + } + } + } + // Default policy: + // External URLs are not allowed + return false; + } + + /** + * Called when the URL of the webview changes. + * + * @param url The URL that is being changed to. + * @return Return false to allow the URL to load, return true to prevent the URL from loading. + */ + public boolean onOverrideUrlLoading(String url) { + synchronized (this.entryMap) { + for (PluginEntry entry : this.entryMap.values()) { + CordovaPlugin plugin = pluginMap.get(entry.service); + if (plugin != null && plugin.onOverrideUrlLoading(url)) { + return true; + } + } + return false; + } + } + + /** + * Called when the app navigates or refreshes. + */ + public void onReset() { + synchronized (this.pluginMap) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + plugin.onReset(); + } + } + } + } + + Uri remapUri(Uri uri) { + synchronized (this.pluginMap) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + Uri ret = plugin.remapUri(uri); + if (ret != null) { + return ret; + } + } + } + } + return null; + } + + /** + * Create a plugin based on class name. + */ + private CordovaPlugin instantiatePlugin(String className) { + CordovaPlugin ret = null; + try { + Class c = null; + if ((className != null) && !("".equals(className))) { + c = Class.forName(className); + } + if (c != null & CordovaPlugin.class.isAssignableFrom(c)) { + ret = (CordovaPlugin) c.newInstance(); + } + } catch (Exception e) { + e.printStackTrace(); + System.out.println("Error adding plugin " + className + "."); + } + return ret; + } + + /** + * Called by the system when the device configuration changes while your activity is running. + * + * @param newConfig The new device configuration + */ + public void onConfigurationChanged(Configuration newConfig) { + synchronized (this.pluginMap) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + plugin.onConfigurationChanged(newConfig); + } + } + } + } + + public Bundle onSaveInstanceState() { + Bundle state = new Bundle(); + synchronized (this.pluginMap) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + Bundle pluginState = plugin.onSaveInstanceState(); + if (pluginState != null) { + state.putBundle(plugin.getServiceName(), pluginState); + } + } + } + } + return state; + } + + /** + * Collect all plugins PathHandlers + * + * @return list of PathHandlers in no particular order + */ + public ArrayList getPluginPathHandlers() { + ArrayList handlers = new ArrayList(); + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null && plugin.getPathHandler() != null) { + handlers.add(plugin.getPathHandler()); + } + } + return handlers; + } + + /** + * Called when the WebView's render process has exited. + * + * See https://developer.android.com/reference/android/webkit/WebViewClient#onRenderProcessGone(android.webkit.WebView,%20android.webkit.RenderProcessGoneDetail) + * + * @return true if the host application handled the situation that process has exited, + * otherwise, application will crash if render process crashed, or be killed + * if render process was killed by the system. + */ + public boolean onRenderProcessGone(final WebView view, RenderProcessGoneDetail detail) { + boolean result = false; + synchronized (this.entryMap) { + for (PluginEntry entry : this.entryMap.values()) { + CordovaPlugin plugin = pluginMap.get(entry.service); + if (plugin != null) { + if (plugin.onRenderProcessGone(view, detail)) { + result = true; + } + } + } + } + + return result; + } +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/PluginResult.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/PluginResult.java new file mode 100644 index 00000000..5db60b3b --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/PluginResult.java @@ -0,0 +1,198 @@ +/* + 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 org.apache.cordova; + +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONObject; + +import android.util.Base64; + +public class PluginResult { + private final int status; + private final int messageType; + private boolean keepCallback = false; + private String strMessage; + private String encodedMessage; + private List multipartMessages; + + public PluginResult(Status status) { + this(status, PluginResult.StatusMessages[status.ordinal()]); + } + + public PluginResult(Status status, String message) { + this.status = status.ordinal(); + this.messageType = message == null ? MESSAGE_TYPE_NULL : MESSAGE_TYPE_STRING; + this.strMessage = message; + } + + public PluginResult(Status status, JSONArray message) { + this.status = status.ordinal(); + this.messageType = MESSAGE_TYPE_JSON; + encodedMessage = message.toString(); + } + + public PluginResult(Status status, JSONObject message) { + this.status = status.ordinal(); + this.messageType = MESSAGE_TYPE_JSON; + encodedMessage = message.toString(); + } + + public PluginResult(Status status, int i) { + this.status = status.ordinal(); + this.messageType = MESSAGE_TYPE_NUMBER; + this.encodedMessage = ""+i; + } + + public PluginResult(Status status, float f) { + this.status = status.ordinal(); + this.messageType = MESSAGE_TYPE_NUMBER; + this.encodedMessage = ""+f; + } + + public PluginResult(Status status, boolean b) { + this.status = status.ordinal(); + this.messageType = MESSAGE_TYPE_BOOLEAN; + this.encodedMessage = Boolean.toString(b); + } + + public PluginResult(Status status, byte[] data) { + this(status, data, false); + } + + public PluginResult(Status status, byte[] data, boolean binaryString) { + this.status = status.ordinal(); + this.messageType = binaryString ? MESSAGE_TYPE_BINARYSTRING : MESSAGE_TYPE_ARRAYBUFFER; + this.encodedMessage = Base64.encodeToString(data, Base64.NO_WRAP); + } + + // The keepCallback and status of multipartMessages are ignored. + public PluginResult(Status status, List multipartMessages) { + this.status = status.ordinal(); + this.messageType = MESSAGE_TYPE_MULTIPART; + this.multipartMessages = multipartMessages; + } + + public void setKeepCallback(boolean b) { + this.keepCallback = b; + } + + public int getStatus() { + return status; + } + + public int getMessageType() { + return messageType; + } + + public String getMessage() { + if (encodedMessage == null) { + encodedMessage = JSONObject.quote(strMessage); + } + return encodedMessage; + } + + public int getMultipartMessagesSize() { + return multipartMessages.size(); + } + + public PluginResult getMultipartMessage(int index) { + return multipartMessages.get(index); + } + + /** + * If messageType == MESSAGE_TYPE_STRING, then returns the message string. + * Otherwise, returns null. + */ + public String getStrMessage() { + return strMessage; + } + + public boolean getKeepCallback() { + return this.keepCallback; + } + + @Deprecated // Use sendPluginResult instead of sendJavascript. + public String getJSONString() { + return "{\"status\":" + this.status + ",\"message\":" + this.getMessage() + ",\"keepCallback\":" + this.keepCallback + "}"; + } + + @Deprecated // Use sendPluginResult instead of sendJavascript. + public String toCallbackString(String callbackId) { + // If no result to be sent and keeping callback, then no need to sent back to JavaScript + if ((status == PluginResult.Status.NO_RESULT.ordinal()) && keepCallback) { + return null; + } + + // Check the success (OK, NO_RESULT & !KEEP_CALLBACK) + if ((status == PluginResult.Status.OK.ordinal()) || (status == PluginResult.Status.NO_RESULT.ordinal())) { + return toSuccessCallbackString(callbackId); + } + + return toErrorCallbackString(callbackId); + } + + @Deprecated // Use sendPluginResult instead of sendJavascript. + public String toSuccessCallbackString(String callbackId) { + return "cordova.callbackSuccess('"+callbackId+"',"+this.getJSONString()+");"; + } + + @Deprecated // Use sendPluginResult instead of sendJavascript. + public String toErrorCallbackString(String callbackId) { + return "cordova.callbackError('"+callbackId+"', " + this.getJSONString()+ ");"; + } + + public static final int MESSAGE_TYPE_STRING = 1; + public static final int MESSAGE_TYPE_JSON = 2; + public static final int MESSAGE_TYPE_NUMBER = 3; + public static final int MESSAGE_TYPE_BOOLEAN = 4; + public static final int MESSAGE_TYPE_NULL = 5; + public static final int MESSAGE_TYPE_ARRAYBUFFER = 6; + // Use BINARYSTRING when your string may contain null characters. + // This is required to work around a bug in the platform :(. + public static final int MESSAGE_TYPE_BINARYSTRING = 7; + public static final int MESSAGE_TYPE_MULTIPART = 8; + + public static String[] StatusMessages = new String[] { + "No result", + "OK", + "Class not found", + "Illegal access", + "Instantiation error", + "Malformed url", + "IO error", + "Invalid action", + "JSON error", + "Error" + }; + + public enum Status { + NO_RESULT, + OK, + CLASS_NOT_FOUND_EXCEPTION, + ILLEGAL_ACCESS_EXCEPTION, + INSTANTIATION_EXCEPTION, + MALFORMED_URL_EXCEPTION, + IO_EXCEPTION, + INVALID_ACTION, + JSON_EXCEPTION, + ERROR + } +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/ResumeCallback.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/ResumeCallback.java new file mode 100644 index 00000000..49a43b5d --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/ResumeCallback.java @@ -0,0 +1,76 @@ +/* + 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 org.apache.cordova; + + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +public class ResumeCallback extends CallbackContext { + private final String TAG = "CordovaResumeCallback"; + private String serviceName; + private PluginManager pluginManager; + + public ResumeCallback(String serviceName, PluginManager pluginManager) { + super("resumecallback", null); + this.serviceName = serviceName; + this.pluginManager = pluginManager; + } + + @Override + public void sendPluginResult(PluginResult pluginResult) { + synchronized (this) { + if (finished) { + LOG.w(TAG, serviceName + " attempted to send a second callback to ResumeCallback\nResult was: " + pluginResult.getMessage()); + return; + } else { + finished = true; + } + } + + JSONObject event = new JSONObject(); + JSONObject pluginResultObject = new JSONObject(); + + try { + pluginResultObject.put("pluginServiceName", this.serviceName); + pluginResultObject.put("pluginStatus", PluginResult.StatusMessages[pluginResult.getStatus()]); + + event.put("action", "resume"); + event.put("pendingResult", pluginResultObject); + } catch (JSONException e) { + LOG.e(TAG, "Unable to create resume object for Activity Result"); + } + + PluginResult eventResult = new PluginResult(PluginResult.Status.OK, event); + + // We send a list of results to the js so that we don't have to decode + // the PluginResult passed to this CallbackContext into JSON twice. + // The results are combined into an event payload before the event is + // fired on the js side of things (see platform.js) + List result = new ArrayList(); + result.add(eventResult); + result.add(pluginResult); + + CoreAndroid appPlugin = (CoreAndroid) pluginManager.getPlugin(CoreAndroid.PLUGIN_NAME); + appPlugin.sendResumeEvent(new PluginResult(PluginResult.Status.OK, result)); + } +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/SplashScreenPlugin.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/SplashScreenPlugin.java new file mode 100644 index 00000000..425b13f9 --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/SplashScreenPlugin.java @@ -0,0 +1,170 @@ +/* + 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 org.apache.cordova; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.annotation.SuppressLint; +import android.os.Handler; +import android.view.View; +import android.view.animation.AccelerateInterpolator; + +import androidx.annotation.NonNull; +import androidx.core.splashscreen.SplashScreen; +import androidx.core.splashscreen.SplashScreenViewProvider; + +import org.json.JSONArray; +import org.json.JSONException; + +@SuppressLint("LongLogTag") +public class SplashScreenPlugin extends CordovaPlugin { + static final String PLUGIN_NAME = "CordovaSplashScreenPlugin"; + + // Default config preference values + private static final boolean DEFAULT_AUTO_HIDE = true; + private static final int DEFAULT_DELAY_TIME = -1; + private static final boolean DEFAULT_FADE = true; + private static final int DEFAULT_FADE_TIME = 500; + + // Config preference values + /** + * @param boolean autoHide to auto splash screen (default=true) + */ + private boolean autoHide; + /** + * @param int delayTime in milliseconds (default=-1) + */ + private int delayTime; + /** + * @param int fade to fade out splash screen (default=true) + */ + private boolean isFadeEnabled; + /** + * @param int fadeDuration fade out duration in milliseconds (default=500) + */ + private int fadeDuration; + + // Internal variables + /** + * @param boolean keepOnScreen flag to determine if the splash screen remains visible. + */ + private boolean keepOnScreen = true; + + @Override + protected void pluginInitialize() { + // Auto Hide & Delay Settings + autoHide = preferences.getBoolean("AutoHideSplashScreen", DEFAULT_AUTO_HIDE); + delayTime = preferences.getInteger("SplashScreenDelay", DEFAULT_DELAY_TIME); + LOG.d(PLUGIN_NAME, "Auto Hide: " + autoHide); + if (delayTime != DEFAULT_DELAY_TIME) { + LOG.d(PLUGIN_NAME, "Delay: " + delayTime + "ms"); + } + + // Fade & Fade Duration + isFadeEnabled = preferences.getBoolean("FadeSplashScreen", DEFAULT_FADE); + fadeDuration = preferences.getInteger("FadeSplashScreenDuration", DEFAULT_FADE_TIME); + LOG.d(PLUGIN_NAME, "Fade: " + isFadeEnabled); + if (isFadeEnabled) { + LOG.d(PLUGIN_NAME, "Fade Duration: " + fadeDuration + "ms"); + } + } + + @Override + public boolean execute( + String action, + JSONArray args, + CallbackContext callbackContext + ) throws JSONException { + if (action.equals("hide") && autoHide == false) { + /* + * The `.hide()` method can only be triggered if the `splashScreenAutoHide` + * is set to `false`. + */ + keepOnScreen = false; + } else { + return false; + } + + callbackContext.success(); + return true; + } + + @Override + public Object onMessage(String id, Object data) { + switch (id) { + case "setupSplashScreen": + setupSplashScreen((SplashScreen) data); + break; + + case "onPageFinished": + attemptCloseOnPageFinished(); + break; + } + + return null; + } + + private void setupSplashScreen(SplashScreen splashScreen) { + // Setup Splash Screen Delay + splashScreen.setKeepOnScreenCondition(() -> keepOnScreen); + + // auto hide splash screen when custom delay is defined. + if (autoHide && delayTime != DEFAULT_DELAY_TIME) { + Handler splashScreenDelayHandler = new Handler(cordova.getContext().getMainLooper()); + splashScreenDelayHandler.postDelayed(() -> keepOnScreen = false, delayTime); + } + + // auto hide splash screen with default delay (-1) delay is controlled by the + // `onPageFinished` message. + + // If auto hide is disabled (false), the hiding of the splash screen must be determined & + // triggered by the front-end code with the `navigator.splashscreen.hide()` method. + + if (isFadeEnabled) { + // Setup the fade + splashScreen.setOnExitAnimationListener(new SplashScreen.OnExitAnimationListener() { + @Override + public void onSplashScreenExit(@NonNull SplashScreenViewProvider splashScreenViewProvider) { + View splashScreenView = splashScreenViewProvider.getView(); + + splashScreenView + .animate() + .alpha(0.0f) + .setDuration(fadeDuration) + .setStartDelay(0) + .setInterpolator(new AccelerateInterpolator()) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + splashScreenViewProvider.remove(); + } + }).start(); + } + }); + } + } + + private void attemptCloseOnPageFinished() { + if (autoHide && delayTime == DEFAULT_DELAY_TIME) { + keepOnScreen = false; + } + } +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/engine/SystemCookieManager.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/engine/SystemCookieManager.java new file mode 100644 index 00000000..bc980356 --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/engine/SystemCookieManager.java @@ -0,0 +1,63 @@ +/* + 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 org.apache.cordova.engine; + +import android.webkit.CookieManager; +import android.webkit.WebView; + +import org.apache.cordova.ICordovaCookieManager; + +class SystemCookieManager implements ICordovaCookieManager { + + protected final WebView webView; + private final CookieManager cookieManager; + + public SystemCookieManager(WebView webview) { + webView = webview; + cookieManager = CookieManager.getInstance(); + + cookieManager.setAcceptThirdPartyCookies(webView, true); + } + + @SuppressWarnings("deprecation") + public void setAcceptFileSchemeCookies() { + cookieManager.setAcceptFileSchemeCookies(true); + } + + public void setCookiesEnabled(boolean accept) { + cookieManager.setAcceptCookie(accept); + } + + public void setCookie(final String url, final String value) { + cookieManager.setCookie(url, value); + } + + public String getCookie(final String url) { + return cookieManager.getCookie(url); + } + + public void clearCookies() { + cookieManager.removeAllCookies(null); + } + + public void flush() { + cookieManager.flush(); + } +}; diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/engine/SystemExposedJsApi.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/engine/SystemExposedJsApi.java new file mode 100755 index 00000000..94c3d934 --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/engine/SystemExposedJsApi.java @@ -0,0 +1,53 @@ +/* + 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 org.apache.cordova.engine; + +import android.webkit.JavascriptInterface; + +import org.apache.cordova.CordovaBridge; +import org.apache.cordova.ExposedJsApi; +import org.json.JSONException; + +/** + * Contains APIs that the JS can call. All functions in here should also have + * an equivalent entry in CordovaChromeClient.java, and be added to + * cordova-js/lib/android/plugin/android/promptbasednativeapi.js + */ +class SystemExposedJsApi implements ExposedJsApi { + private final CordovaBridge bridge; + + SystemExposedJsApi(CordovaBridge bridge) { + this.bridge = bridge; + } + + @JavascriptInterface + public String exec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException { + return bridge.jsExec(bridgeSecret, service, action, callbackId, arguments); + } + + @JavascriptInterface + public void setNativeToJsBridgeMode(int bridgeSecret, int value) throws IllegalAccessException { + bridge.jsSetNativeToJsBridgeMode(bridgeSecret, value); + } + + @JavascriptInterface + public String retrieveJsMessages(int bridgeSecret, boolean fromOnlineEvent) throws IllegalAccessException { + return bridge.jsRetrieveJsMessages(bridgeSecret, fromOnlineEvent); + } +} diff --git a/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/engine/SystemWebChromeClient.java b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/engine/SystemWebChromeClient.java new file mode 100755 index 00000000..cad098e4 --- /dev/null +++ b/MovieVerse-Mobile/platforms/android/CordovaLib/src/org/apache/cordova/engine/SystemWebChromeClient.java @@ -0,0 +1,270 @@ +/* + 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 org.apache.cordova.engine; + +import java.util.Arrays; +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; +import android.content.ActivityNotFoundException; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup.LayoutParams; +import android.webkit.ConsoleMessage; +import android.webkit.GeolocationPermissions.Callback; +import android.webkit.JsPromptResult; +import android.webkit.JsResult; +import android.webkit.ValueCallback; +import android.webkit.WebChromeClient; +import android.webkit.WebStorage; +import android.webkit.WebView; +import android.webkit.PermissionRequest; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; + +import org.apache.cordova.CordovaDialogsHelper; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.LOG; + +/** + * This class is the WebChromeClient that implements callbacks for our web view. + * The kind of callbacks that happen here are on the chrome outside the document, + * such as onCreateWindow(), onConsoleMessage(), onProgressChanged(), etc. Related + * to but different than CordovaWebViewClient. + */ +public class SystemWebChromeClient extends WebChromeClient { + + private static final int FILECHOOSER_RESULTCODE = 5173; + private static final String LOG_TAG = "SystemWebChromeClient"; + private long MAX_QUOTA = 100 * 1024 * 1024; + protected final SystemWebViewEngine parentEngine; + + // the video progress view + private View mVideoProgressView; + + private CordovaDialogsHelper dialogsHelper; + private Context appContext; + + private WebChromeClient.CustomViewCallback mCustomViewCallback; + private View mCustomView; + + public SystemWebChromeClient(SystemWebViewEngine parentEngine) { + this.parentEngine = parentEngine; + appContext = parentEngine.webView.getContext(); + dialogsHelper = new CordovaDialogsHelper(appContext); + } + + /** + * Tell the client to display a javascript alert dialog. + */ + @Override + public boolean onJsAlert(WebView view, String url, String message, final JsResult result) { + dialogsHelper.showAlert(message, new CordovaDialogsHelper.Result() { + @Override public void gotResult(boolean success, String value) { + if (success) { + result.confirm(); + } else { + result.cancel(); + } + } + }); + return true; + } + + /** + * Tell the client to display a confirm dialog to the user. + */ + @Override + public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) { + dialogsHelper.showConfirm(message, new CordovaDialogsHelper.Result() { + @Override + public void gotResult(boolean success, String value) { + if (success) { + result.confirm(); + } else { + result.cancel(); + } + } + }); + return true; + } + + /** + * Tell the client to display a prompt dialog to the user. + * If the client returns true, WebView will assume that the client will + * handle the prompt dialog and call the appropriate JsPromptResult method. + * + * Since we are hacking prompts for our own purposes, we should not be using them for + * this purpose, perhaps we should hack console.log to do this instead! + */ + @Override + public boolean onJsPrompt(WebView view, String origin, String message, String defaultValue, final JsPromptResult result) { + // Unlike the @JavascriptInterface bridge, this method is always called on the UI thread. + String handledRet = parentEngine.bridge.promptOnJsPrompt(origin, message, defaultValue); + if (handledRet != null) { + result.confirm(handledRet); + } else { + dialogsHelper.showPrompt(message, defaultValue, new CordovaDialogsHelper.Result() { + @Override + public void gotResult(boolean success, String value) { + if (success) { + result.confirm(value); + } else { + result.cancel(); + } + } + }); + } + return true; + } + + /** + * Handle database quota exceeded notification. + */ + @Override + @SuppressWarnings("deprecation") + public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize, + long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) + { + LOG.d(LOG_TAG, "onExceededDatabaseQuota estimatedSize: %d currentQuota: %d totalUsedQuota: %d", estimatedSize, currentQuota, totalUsedQuota); + quotaUpdater.updateQuota(MAX_QUOTA); + } + + @Override + /** + * Instructs the client to show a prompt to ask the user to set the Geolocation permission state for the specified origin. + * + * This also checks for the Geolocation Plugin and requests permission from the application to use Geolocation. + * + * @param origin + * @param callback + */ + public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) { + super.onGeolocationPermissionsShowPrompt(origin, callback); + callback.invoke(origin, true, false); + //Get the plugin, it should be loaded + CordovaPlugin geolocation = parentEngine.pluginManager.getPlugin("Geolocation"); + if(geolocation != null && !geolocation.hasPermisssion()) + { + geolocation.requestPermissions(0); + } + } + + // API level 7 is required for this, see if we could lower this using something else + @Override + @SuppressWarnings("deprecation") + public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) { + parentEngine.getCordovaWebView().showCustomView(view, callback); + } + + @Override + @SuppressWarnings("deprecation") + public void onHideCustomView() { + parentEngine.getCordovaWebView().hideCustomView(); + } + + @Override + /** + * Ask the host application for a custom progress view to show while + * a

" + @"

The WebView engine '%@' is unable to load the request: %@

" + @"

Most likely the cause of the error is that the loading of file urls is not supported in iOS %@.

" + @"
", + NSStringFromClass([self class]), + [request.URL description], + [[UIDevice currentDevice] systemVersion] + ]; + return [self loadHTMLString:errorHtml baseURL:nil]; + } +} + +- (id)loadHTMLString:(NSString*)string baseURL:(NSURL*)baseURL +{ + return [(WKWebView*)_engineWebView loadHTMLString:string baseURL:baseURL]; +} + +- (NSURL*) URL +{ + return [(WKWebView*)_engineWebView URL]; +} + +- (BOOL) canLoadRequest:(NSURLRequest*)request +{ + // See: https://issues.apache.org/jira/browse/CB-9636 + SEL wk_sel = NSSelectorFromString(CDV_WKWEBVIEW_FILE_URL_LOAD_SELECTOR); + + // if it's a file URL, check whether WKWebView has the selector (which is in iOS 9 and up only) + if (request.URL.fileURL) { + return [_engineWebView respondsToSelector:wk_sel]; + } else { + return YES; + } +} + +- (void)updateSettings:(NSDictionary*)settings +{ + WKWebView* wkWebView = (WKWebView*)_engineWebView; + + wkWebView.configuration.preferences.minimumFontSize = [settings cordovaFloatSettingForKey:@"MinimumFontSize" defaultValue:0.0]; + + /* + wkWebView.configuration.preferences.javaScriptEnabled = [settings cordovaBoolSettingForKey:@"JavaScriptEnabled" default:YES]; + wkWebView.configuration.preferences.javaScriptCanOpenWindowsAutomatically = [settings cordovaBoolSettingForKey:@"JavaScriptCanOpenWindowsAutomatically" default:NO]; + */ + + // By default, DisallowOverscroll is false (thus bounce is allowed) + BOOL bounceAllowed = !([settings cordovaBoolSettingForKey:@"DisallowOverscroll" defaultValue:NO]); + + // prevent webView from bouncing + if (!bounceAllowed) { + if ([wkWebView respondsToSelector:@selector(scrollView)]) { + UIScrollView* scrollView = [wkWebView scrollView]; + scrollView.bounces = NO; + scrollView.alwaysBounceVertical = NO; /* iOS 16 workaround */ + scrollView.alwaysBounceHorizontal = NO; /* iOS 16 workaround */ + } else { + for (id subview in wkWebView.subviews) { + if ([[subview class] isSubclassOfClass:[UIScrollView class]]) { + ((UIScrollView*)subview).bounces = NO; + } + } + } + } + + NSString* decelerationSetting = [settings cordovaSettingForKey:@"WKWebViewDecelerationSpeed"]; + + if (![@"fast" isEqualToString:decelerationSetting]) { + [wkWebView.scrollView setDecelerationRate:UIScrollViewDecelerationRateNormal]; + } else { + [wkWebView.scrollView setDecelerationRate:UIScrollViewDecelerationRateFast]; + } + + wkWebView.allowsBackForwardNavigationGestures = [settings cordovaBoolSettingForKey:@"AllowBackForwardNavigationGestures" defaultValue:NO]; + wkWebView.allowsLinkPreview = [settings cordovaBoolSettingForKey:@"Allow3DTouchLinkPreview" defaultValue:YES]; +} + +- (void)updateWithInfo:(NSDictionary*)info +{ + NSDictionary* scriptMessageHandlers = [info objectForKey:kCDVWebViewEngineScriptMessageHandlers]; + NSDictionary* settings = [info objectForKey:kCDVWebViewEngineWebViewPreferences]; + id navigationDelegate = [info objectForKey:kCDVWebViewEngineWKNavigationDelegate]; + id uiDelegate = [info objectForKey:kCDVWebViewEngineWKUIDelegate]; + + WKWebView* wkWebView = (WKWebView*)_engineWebView; + + if (scriptMessageHandlers && [scriptMessageHandlers isKindOfClass:[NSDictionary class]]) { + NSArray* allKeys = [scriptMessageHandlers allKeys]; + + for (NSString* key in allKeys) { + id object = [scriptMessageHandlers objectForKey:key]; + if ([object conformsToProtocol:@protocol(WKScriptMessageHandler)]) { + [wkWebView.configuration.userContentController addScriptMessageHandler:object name:key]; + } + } + } + + if (navigationDelegate && [navigationDelegate conformsToProtocol:@protocol(WKNavigationDelegate)]) { + wkWebView.navigationDelegate = navigationDelegate; + } + + if (uiDelegate && [uiDelegate conformsToProtocol:@protocol(WKUIDelegate)]) { + wkWebView.UIDelegate = uiDelegate; + } + + if (settings && [settings isKindOfClass:[NSDictionary class]]) { + [self updateSettings:settings]; + } +} + +// This forwards the methods that are in the header that are not implemented here. +// Both WKWebView implement the below: +// loadHTMLString:baseURL: +// loadRequest: +- (id)forwardingTargetForSelector:(SEL)aSelector +{ + return _engineWebView; +} + +- (UIView*)webView +{ + return self.engineWebView; +} + +#pragma mark WKScriptMessageHandler implementation + +- (void)userContentController:(WKUserContentController*)userContentController didReceiveScriptMessage:(WKScriptMessage*)message +{ + if (![message.name isEqualToString:CDV_BRIDGE_NAME]) { + return; + } + + CDVViewController* vc = (CDVViewController*)self.viewController; + + NSArray* jsonEntry = message.body; // NSString:callbackId, NSString:service, NSString:action, NSArray:args + CDVInvokedUrlCommand* command = [CDVInvokedUrlCommand commandFromJson:jsonEntry]; + CDV_EXEC_LOG(@"Exec(%@): Calling %@.%@", command.callbackId, command.className, command.methodName); + + if (![vc.commandQueue execute:command]) { +#ifdef DEBUG + NSError* error = nil; + NSString* commandJson = nil; + NSData* jsonData = [NSJSONSerialization dataWithJSONObject:jsonEntry + options:0 + error:&error]; + + if (error == nil) { + commandJson = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + } + + static NSUInteger maxLogLength = 1024; + NSString* commandString = ([commandJson length] > maxLogLength) ? + [NSString stringWithFormat : @"%@[...]", [commandJson substringToIndex:maxLogLength]] : + commandJson; + + NSLog(@"FAILED pluginJSON = %@", commandString); +#endif + } +} + +#pragma mark WKNavigationDelegate implementation + +- (void)webView:(WKWebView*)webView didStartProvisionalNavigation:(WKNavigation*)navigation +{ + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginResetNotification object:webView]]; +} + +- (void)webView:(WKWebView*)webView didFinishNavigation:(WKNavigation*)navigation +{ + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPageDidLoadNotification object:webView]]; +} + +- (void)webView:(WKWebView*)theWebView didFailProvisionalNavigation:(WKNavigation*)navigation withError:(NSError*)error +{ + [self webView:theWebView didFailNavigation:navigation withError:error]; +} + +- (void)webView:(WKWebView*)theWebView didFailNavigation:(WKNavigation*)navigation withError:(NSError*)error +{ + CDVViewController* vc = (CDVViewController*)self.viewController; + + NSString* message = [NSString stringWithFormat:@"Failed to load webpage with error: %@", [error localizedDescription]]; + NSLog(@"%@", message); + + NSURL* errorUrl = vc.errorURL; + if (errorUrl) { + NSCharacterSet *charSet = [NSCharacterSet URLFragmentAllowedCharacterSet]; + errorUrl = [NSURL URLWithString:[NSString stringWithFormat:@"?error=%@", [message stringByAddingPercentEncodingWithAllowedCharacters:charSet]] relativeToURL:errorUrl]; + NSLog(@"%@", [errorUrl absoluteString]); + [theWebView loadRequest:[NSURLRequest requestWithURL:errorUrl]]; + } +} + +- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView +{ + [webView reload]; +} + +- (BOOL)defaultResourcePolicyForURL:(NSURL*)url +{ + // all file:// urls are allowed + if ([url isFileURL]) { + return YES; + } + + return NO; +} + +- (void) webView: (WKWebView *) webView decidePolicyForNavigationAction: (WKNavigationAction*) navigationAction decisionHandler: (void (^)(WKNavigationActionPolicy)) decisionHandler +{ + NSURL* url = [navigationAction.request URL]; + CDVViewController* vc = (CDVViewController*)self.viewController; + + /* + * Give plugins the chance to handle the url + */ + BOOL anyPluginsResponded = NO; + BOOL shouldAllowRequest = NO; + + for (NSString* pluginName in vc.pluginObjects) { + CDVPlugin* plugin = [vc.pluginObjects objectForKey:pluginName]; + SEL selector = NSSelectorFromString(@"shouldOverrideLoadWithRequest:navigationType:"); + if ([plugin respondsToSelector:selector]) { + anyPluginsResponded = YES; + // https://issues.apache.org/jira/browse/CB-12497 + int navType = (int)navigationAction.navigationType; + shouldAllowRequest = (((BOOL (*)(id, SEL, id, int))objc_msgSend)(plugin, selector, navigationAction.request, navType)); + if (!shouldAllowRequest) { + break; + } + } + } + + if (anyPluginsResponded) { + return decisionHandler(shouldAllowRequest); + } + + /* + * Handle all other types of urls (tel:, sms:), and requests to load a url in the main webview. + */ + BOOL shouldAllowNavigation = [self defaultResourcePolicyForURL:url]; + if (shouldAllowNavigation) { + return decisionHandler(YES); + } else { + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]]; + } + + return decisionHandler(NO); +} + +#pragma mark - Plugin interface + +- (void)allowsBackForwardNavigationGestures:(CDVInvokedUrlCommand*)command; +{ + id value = [command argumentAtIndex:0]; + if (!([value isKindOfClass:[NSNumber class]])) { + value = [NSNumber numberWithBool:NO]; + } + + WKWebView* wkWebView = (WKWebView*)_engineWebView; + wkWebView.allowsBackForwardNavigationGestures = [value boolValue]; +} + +@end + +#pragma mark - CDVWebViewWeakScriptMessageHandler + +@implementation CDVWebViewWeakScriptMessageHandler + +- (instancetype)initWithScriptMessageHandler:(id)scriptMessageHandler +{ + self = [super init]; + if (self) { + _scriptMessageHandler = scriptMessageHandler; + } + return self; +} + +- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message +{ + [self.scriptMessageHandler userContentController:userContentController didReceiveScriptMessage:message]; +} + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewUIDelegate.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewUIDelegate.h new file mode 100644 index 00000000..c5f85fd7 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewUIDelegate.h @@ -0,0 +1,32 @@ +/* + 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. + */ + +@import WebKit; + +@interface CDVWebViewUIDelegate : NSObject +{ + NSMutableArray* windows; +} + +@property (nonatomic, copy) NSString* title; +@property (nonatomic, assign) BOOL allowNewWindows; + +- (instancetype)initWithTitle:(NSString*)title; + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewUIDelegate.m b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewUIDelegate.m new file mode 100644 index 00000000..784af8df --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewUIDelegate.m @@ -0,0 +1,163 @@ +/* + 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. + */ + +#import "CDVWebViewUIDelegate.h" + +@implementation CDVWebViewUIDelegate + +- (instancetype)initWithTitle:(NSString*)title +{ + self = [super init]; + if (self) { + self.title = title; + windows = [[NSMutableArray alloc] init]; + } + + return self; +} + +- (void) webView:(WKWebView*)webView runJavaScriptAlertPanelWithMessage:(NSString*)message + initiatedByFrame:(WKFrameInfo*)frame completionHandler:(void (^)(void))completionHandler +{ + UIAlertController* alert = [UIAlertController alertControllerWithTitle:self.title + message:message + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction* ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"OK") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction* action) + { + completionHandler(); + [alert dismissViewControllerAnimated:YES completion:nil]; + }]; + + [alert addAction:ok]; + + UIViewController* rootController = [UIApplication sharedApplication].delegate.window.rootViewController; + + [rootController presentViewController:alert animated:YES completion:nil]; +} + +- (void) webView:(WKWebView*)webView runJavaScriptConfirmPanelWithMessage:(NSString*)message + initiatedByFrame:(WKFrameInfo*)frame completionHandler:(void (^)(BOOL result))completionHandler +{ + UIAlertController* alert = [UIAlertController alertControllerWithTitle:self.title + message:message + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction* ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"OK") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction* action) + { + completionHandler(YES); + [alert dismissViewControllerAnimated:YES completion:nil]; + }]; + + [alert addAction:ok]; + + UIAlertAction* cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", @"Cancel") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction* action) + { + completionHandler(NO); + [alert dismissViewControllerAnimated:YES completion:nil]; + }]; + [alert addAction:cancel]; + + UIViewController* rootController = [UIApplication sharedApplication].delegate.window.rootViewController; + + [rootController presentViewController:alert animated:YES completion:nil]; +} + +- (void) webView:(WKWebView*)webView runJavaScriptTextInputPanelWithPrompt:(NSString*)prompt + defaultText:(NSString*)defaultText initiatedByFrame:(WKFrameInfo*)frame + completionHandler:(void (^)(NSString* result))completionHandler +{ + UIAlertController* alert = [UIAlertController alertControllerWithTitle:self.title + message:prompt + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction* ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"OK") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction* action) + { + completionHandler(((UITextField*)alert.textFields[0]).text); + [alert dismissViewControllerAnimated:YES completion:nil]; + }]; + + [alert addAction:ok]; + + UIAlertAction* cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", @"Cancel") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction* action) + { + completionHandler(nil); + [alert dismissViewControllerAnimated:YES completion:nil]; + }]; + [alert addAction:cancel]; + + [alert addTextFieldWithConfigurationHandler:^(UITextField* textField) { + textField.text = defaultText; + }]; + + UIViewController* rootController = [UIApplication sharedApplication].delegate.window.rootViewController; + + [rootController presentViewController:alert animated:YES completion:nil]; +} + +- (WKWebView*) webView:(WKWebView*)webView createWebViewWithConfiguration:(WKWebViewConfiguration*)configuration forNavigationAction:(WKNavigationAction*)navigationAction windowFeatures:(WKWindowFeatures*)windowFeatures +{ + if (!navigationAction.targetFrame.isMainFrame) { + if (self.allowNewWindows) { + WKWebView* v = [[WKWebView alloc] initWithFrame:webView.frame configuration:configuration]; + v.UIDelegate = webView.UIDelegate; + v.navigationDelegate = webView.navigationDelegate; + + UIViewController* vc = [[UIViewController alloc] init]; + vc.modalPresentationStyle = UIModalPresentationOverCurrentContext; + vc.view = v; + + [windows addObject:vc]; + + UIViewController* rootController = [UIApplication sharedApplication].delegate.window.rootViewController; + [rootController presentViewController:vc animated:YES completion:nil]; + return v; + } else { + [webView loadRequest:navigationAction.request]; + } + } + + return nil; +} + +- (void)webViewDidClose:(WKWebView*)webView +{ + for (UIViewController* vc in windows) { + if (vc.view == webView) { + [vc dismissViewControllerAnimated:YES completion:nil]; + [windows removeObject:vc]; + break; + } + } + + // We do not allow closing the primary WebView +} + + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVAllowList.m b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVAllowList.m new file mode 100644 index 00000000..739ca980 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVAllowList.m @@ -0,0 +1,285 @@ +/* + 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. + */ + +#import + +NSString* const kCDVDefaultAllowListRejectionString = @"ERROR allowList rejection: url='%@'"; +NSString* const kCDVDefaultSchemeName = @"cdv-default-scheme"; + +@interface CDVAllowListPattern : NSObject { + @private + NSRegularExpression* _scheme; + NSRegularExpression* _host; + NSNumber* _port; + NSRegularExpression* _path; +} + ++ (NSString*)regexFromPattern:(NSString*)pattern allowWildcards:(bool)allowWildcards; +- (id)initWithScheme:(NSString*)scheme host:(NSString*)host port:(NSString*)port path:(NSString*)path; +- (bool)matches:(NSURL*)url; + +@end + +@implementation CDVAllowListPattern + ++ (NSString*)regexFromPattern:(NSString*)pattern allowWildcards:(bool)allowWildcards +{ + NSString* regex = [NSRegularExpression escapedPatternForString:pattern]; + + if (allowWildcards) { + regex = [regex stringByReplacingOccurrencesOfString:@"\\*" withString:@".*"]; + + /* [NSURL path] has the peculiarity that a trailing slash at the end of a path + * will be omitted. This regex tweak compensates for that. + */ + if ([regex hasSuffix:@"\\/.*"]) { + regex = [NSString stringWithFormat:@"%@(\\/.*)?", [regex substringToIndex:([regex length] - 4)]]; + } + } + return [NSString stringWithFormat:@"%@$", regex]; +} + +- (id)initWithScheme:(NSString*)scheme host:(NSString*)host port:(NSString*)port path:(NSString*)path +{ + self = [super init]; // Potentially change "self" + if (self) { + if ((scheme == nil) || [scheme isEqualToString:@"*"]) { + _scheme = nil; + } else { + _scheme = [NSRegularExpression regularExpressionWithPattern:[CDVAllowListPattern regexFromPattern:scheme allowWildcards:NO] options:NSRegularExpressionCaseInsensitive error:nil]; + } + if ([host isEqualToString:@"*"] || host == nil) { + _host = nil; + } else if ([host hasPrefix:@"*."]) { + _host = [NSRegularExpression regularExpressionWithPattern:[NSString stringWithFormat:@"([a-z0-9.-]*\\.)?%@", [CDVAllowListPattern regexFromPattern:[host substringFromIndex:2] allowWildcards:false]] options:NSRegularExpressionCaseInsensitive error:nil]; + } else { + _host = [NSRegularExpression regularExpressionWithPattern:[CDVAllowListPattern regexFromPattern:host allowWildcards:NO] options:NSRegularExpressionCaseInsensitive error:nil]; + } + if ((port == nil) || [port isEqualToString:@"*"]) { + _port = nil; + } else { + _port = [[NSNumber alloc] initWithInteger:[port integerValue]]; + } + if ((path == nil) || [path isEqualToString:@"/*"]) { + _path = nil; + } else { + _path = [NSRegularExpression regularExpressionWithPattern:[CDVAllowListPattern regexFromPattern:path allowWildcards:YES] options:0 error:nil]; + } + } + return self; +} + +- (bool)matches:(NSURL*)url +{ + return (_scheme == nil || [_scheme numberOfMatchesInString:[url scheme] options:NSMatchingAnchored range:NSMakeRange(0, [[url scheme] length])]) && + (_host == nil || ([url host] != nil && [_host numberOfMatchesInString:[url host] options:NSMatchingAnchored range:NSMakeRange(0, [[url host] length])])) && + (_port == nil || [[url port] isEqualToNumber:_port]) && + (_path == nil || [_path numberOfMatchesInString:[url path] options:NSMatchingAnchored range:NSMakeRange(0, [[url path] length])]) + ; +} + +@end + +@interface CDVAllowList () + +@property (nonatomic, readwrite, strong) NSMutableArray* allowList; +@property (nonatomic, readwrite, strong) NSMutableSet* permittedSchemes; + +- (void)addAllowListEntry:(NSString*)pattern; + +@end + +@implementation CDVAllowList + +@synthesize allowList, permittedSchemes, allowListRejectionFormatString; + +- (id)initWithArray:(NSArray*)array +{ + self = [super init]; + if (self) { + self.allowList = [[NSMutableArray alloc] init]; + self.permittedSchemes = [[NSMutableSet alloc] init]; + self.allowListRejectionFormatString = kCDVDefaultAllowListRejectionString; + + for (NSString* pattern in array) { + [self addAllowListEntry:pattern]; + } + } + return self; +} + +- (BOOL)isIPv4Address:(NSString*)externalHost +{ + // an IPv4 address has 4 octets b.b.b.b where b is a number between 0 and 255. + // for our purposes, b can also be the wildcard character '*' + + // we could use a regex to solve this problem but then I would have two problems + // anyways, this is much clearer and maintainable + NSArray* octets = [externalHost componentsSeparatedByString:@"."]; + NSUInteger num_octets = [octets count]; + + // quick check + if (num_octets != 4) { + return NO; + } + + // restrict number parsing to 0-255 + NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init]; + [numberFormatter setMinimum:[NSNumber numberWithUnsignedInteger:0]]; + [numberFormatter setMaximum:[NSNumber numberWithUnsignedInteger:255]]; + + // iterate through each octet, and test for a number between 0-255 or if it equals '*' + for (NSUInteger i = 0; i < num_octets; ++i) { + NSString* octet = [octets objectAtIndex:i]; + + if ([octet isEqualToString:@"*"]) { // passes - check next octet + continue; + } else if ([numberFormatter numberFromString:octet] == nil) { // fails - not a number and not within our range, return + return NO; + } + } + + return YES; +} + +- (void)addAllowListEntry:(NSString*)origin +{ + if (self.allowList == nil) { + return; + } + + if ([origin isEqualToString:@"*"]) { + NSLog(@"Unlimited access to network resources"); + self.allowList = nil; + self.permittedSchemes = nil; + } else { // specific access + NSRegularExpression* parts = [NSRegularExpression regularExpressionWithPattern:@"^((\\*|[A-Za-z-]+):/?/?)?(((\\*\\.)?[^*/:]+)|\\*)?(:(\\d+))?(/.*)?" options:0 error:nil]; + NSTextCheckingResult* m = [parts firstMatchInString:origin options:NSMatchingAnchored range:NSMakeRange(0, [origin length])]; + if (m != nil) { + NSRange r; + NSString* scheme = nil; + r = [m rangeAtIndex:2]; + if (r.location != NSNotFound) { + scheme = [origin substringWithRange:r]; + } + + NSString* host = nil; + r = [m rangeAtIndex:3]; + if (r.location != NSNotFound) { + host = [origin substringWithRange:r]; + } + + // Special case for two urls which are allowed to have empty hosts + if (([scheme isEqualToString:@"file"] || [scheme isEqualToString:@"content"]) && (host == nil)) { + host = @"*"; + } + + NSString* port = nil; + r = [m rangeAtIndex:7]; + if (r.location != NSNotFound) { + port = [origin substringWithRange:r]; + } + + NSString* path = nil; + r = [m rangeAtIndex:8]; + if (r.location != NSNotFound) { + path = [origin substringWithRange:r]; + } + + if (scheme == nil) { + // XXX making it stupid friendly for people who forget to include protocol/SSL + [self.allowList addObject:[[CDVAllowListPattern alloc] initWithScheme:@"http" host:host port:port path:path]]; + [self.allowList addObject:[[CDVAllowListPattern alloc] initWithScheme:@"https" host:host port:port path:path]]; + } else { + [self.allowList addObject:[[CDVAllowListPattern alloc] initWithScheme:scheme host:host port:port path:path]]; + } + + if (self.permittedSchemes != nil) { + if ([scheme isEqualToString:@"*"]) { + self.permittedSchemes = nil; + } else if (scheme != nil) { + [self.permittedSchemes addObject:scheme]; + } + } + } + } +} + +- (BOOL)schemeIsAllowed:(NSString*)scheme +{ + if ([scheme isEqualToString:@"http"] || + [scheme isEqualToString:@"https"] || + [scheme isEqualToString:@"ftp"] || + [scheme isEqualToString:@"ftps"]) { + return YES; + } + + return (self.permittedSchemes == nil) || [self.permittedSchemes containsObject:scheme]; +} + +- (BOOL)URLIsAllowed:(NSURL*)url +{ + return [self URLIsAllowed:url logFailure:YES]; +} + +- (BOOL)URLIsAllowed:(NSURL*)url logFailure:(BOOL)logFailure +{ + // Shortcut acceptance: Are all urls allowListed ("*" in allowList)? + if (allowList == nil) { + return YES; + } + + // Shortcut rejection: Check that the scheme is supported + NSString* scheme = [[url scheme] lowercaseString]; + if (![self schemeIsAllowed:scheme]) { + if (logFailure) { + NSLog(@"%@", [self errorStringForURL:url]); + } + return NO; + } + + // http[s] and ftp[s] should also validate against the common set in the kCDVDefaultSchemeName list + if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"] || [scheme isEqualToString:@"ftp"] || [scheme isEqualToString:@"ftps"]) { + NSURL* newUrl = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@%@", kCDVDefaultSchemeName, [url host], [[url path] stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLPathAllowedCharacterSet]]]; + // If it is allowed, we are done. If not, continue to check for the actual scheme-specific list + if ([self URLIsAllowed:newUrl logFailure:NO]) { + return YES; + } + } + + // Check the url against patterns in the allowList + for (CDVAllowListPattern* p in self.allowList) { + if ([p matches:url]) { + return YES; + } + } + + if (logFailure) { + NSLog(@"%@", [self errorStringForURL:url]); + } + // if we got here, the url host is not in the white-list, do nothing + return NO; +} + +- (NSString*)errorStringForURL:(NSURL*)url +{ + return [NSString stringWithFormat:self.allowListRejectionFormatString, [url absoluteString]]; +} + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVAppDelegate.m b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVAppDelegate.m new file mode 100644 index 00000000..a77fd2b4 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVAppDelegate.m @@ -0,0 +1,90 @@ +/* + 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. + */ + +#import + +@implementation CDVAppDelegate + +@synthesize window, viewController; + +- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ +#if DEBUG + NSLog(@"Apache Cordova iOS platform version %@ is starting.", CDV_VERSION); +#endif + + return YES; +} + +/** + * This is main kick off after the app inits, the views and Settings are setup here. (preferred - iOS4 and up) + */ +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + CGRect screenBounds = [[UIScreen mainScreen] bounds]; + + self.window = [[UIWindow alloc] initWithFrame:screenBounds]; + self.window.autoresizesSubviews = YES; + + // only set if not already set in subclass + if (self.viewController == nil) { + self.viewController = [[CDVViewController alloc] init]; + } + + // Set your app's start page by setting the tag in config.xml. + // If necessary, uncomment the line below to override it. + // self.viewController.startPage = @"index.html"; + + // NOTE: To customize the view's frame size (which defaults to full screen), override + // [self.viewController viewWillAppear:] in your view controller. + + self.window.rootViewController = self.viewController; + [self.window makeKeyAndVisible]; + + return YES; +} + +// this happens while we are running ( in the background, or from within our own app ) +// only valid if Info.plist specifies a protocol to handle +- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options +{ + if (!url) { + return NO; + } + + NSMutableDictionary * openURLData = [[NSMutableDictionary alloc] init]; + + [openURLData setValue:url forKey:@"url"]; + + if (options[UIApplicationOpenURLOptionsSourceApplicationKey]) { + [openURLData setValue:options[UIApplicationOpenURLOptionsSourceApplicationKey] forKey:@"sourceApplication"]; + } + + if (options[UIApplicationOpenURLOptionsAnnotationKey]) { + [openURLData setValue:options[UIApplicationOpenURLOptionsAnnotationKey] forKey:@"annotation"]; + } + + // all plugins will get the notification, and their handlers will be called + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]]; + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLWithAppSourceAndAnnotationNotification object:openURLData]]; + + return YES; +} + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVCommandQueue.m b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVCommandQueue.m new file mode 100644 index 00000000..98ad3deb --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVCommandQueue.m @@ -0,0 +1,194 @@ +/* + 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. + */ + +#include +#import +#import +#import "CDVCommandDelegateImpl.h" +#import "CDVJSON_private.h" +#import "CDVDebug.h" + +// Parse JS on the main thread if it's shorter than this. +static const NSInteger JSON_SIZE_FOR_MAIN_THREAD = 4 * 1024; // Chosen arbitrarily. +// Execute multiple commands in one go until this many seconds have passed. +static const double MAX_EXECUTION_TIME = .008; // Half of a 60fps frame. + +@interface CDVCommandQueue () { + NSInteger _lastCommandQueueFlushRequestId; + __weak CDVViewController* _viewController; + NSMutableArray* _queue; + NSTimeInterval _startExecutionTime; +} +@end + +@implementation CDVCommandQueue + +- (BOOL)currentlyExecuting +{ + return _startExecutionTime > 0; +} + +- (id)initWithViewController:(CDVViewController*)viewController +{ + self = [super init]; + if (self != nil) { + _viewController = viewController; + _queue = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void)dispose +{ + // TODO(agrieve): Make this a zeroing weak ref once we drop support for 4.3. + _viewController = nil; +} + +- (void)resetRequestId +{ + _lastCommandQueueFlushRequestId = 0; +} + +- (void)enqueueCommandBatch:(NSString*)batchJSON +{ + if ([batchJSON length] > 0) { + NSMutableArray* commandBatchHolder = [[NSMutableArray alloc] init]; + [_queue addObject:commandBatchHolder]; + if ([batchJSON length] < JSON_SIZE_FOR_MAIN_THREAD) { + [commandBatchHolder addObject:[batchJSON cdv_JSONObject]]; + } else { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^() { + NSMutableArray* result = [batchJSON cdv_JSONObject]; + @synchronized(commandBatchHolder) { + [commandBatchHolder addObject:result]; + } + [self performSelectorOnMainThread:@selector(executePending) withObject:nil waitUntilDone:NO]; + }); + } + } +} + +- (void)fetchCommandsFromJs +{ + __weak CDVCommandQueue* weakSelf = self; + NSString* js = @"cordova.require('cordova/exec').nativeFetchMessages()"; + + [_viewController.webViewEngine evaluateJavaScript:js + completionHandler:^(id obj, NSError* error) { + if ((error == nil) && [obj isKindOfClass:[NSString class]]) { + NSString* queuedCommandsJSON = (NSString*)obj; + CDV_EXEC_LOG(@"Exec: Flushed JS->native queue (hadCommands=%d).", [queuedCommandsJSON length] > 0); + [weakSelf enqueueCommandBatch:queuedCommandsJSON]; + // this has to be called here now, because fetchCommandsFromJs is now async (previously: synchronous) + [self executePending]; + } + }]; +} + +- (void)executePending +{ + // Make us re-entrant-safe. + if (_startExecutionTime > 0) { + return; + } + @try { + _startExecutionTime = [NSDate timeIntervalSinceReferenceDate]; + + while ([_queue count] > 0) { + NSMutableArray* commandBatchHolder = _queue[0]; + NSMutableArray* commandBatch = nil; + @synchronized(commandBatchHolder) { + // If the next-up command is still being decoded, wait for it. + if ([commandBatchHolder count] == 0) { + break; + } + commandBatch = commandBatchHolder[0]; + } + + while ([commandBatch count] > 0) { + @autoreleasepool { + // Execute the commands one-at-a-time. + NSArray* jsonEntry = [commandBatch cdv_dequeue]; + if ([commandBatch count] == 0) { + [_queue removeObjectAtIndex:0]; + } + CDVInvokedUrlCommand* command = [CDVInvokedUrlCommand commandFromJson:jsonEntry]; + CDV_EXEC_LOG(@"Exec(%@): Calling %@.%@", command.callbackId, command.className, command.methodName); + + if (![self execute:command]) { +#ifdef DEBUG + NSString* commandJson = [jsonEntry cdv_JSONString]; + static NSUInteger maxLogLength = 1024; + NSString* commandString = ([commandJson length] > maxLogLength) ? + [NSString stringWithFormat : @"%@[...]", [commandJson substringToIndex:maxLogLength]] : + commandJson; + + DLog(@"FAILED pluginJSON = %@", commandString); +#endif + } + } + + // Yield if we're taking too long. + if (([_queue count] > 0) && ([NSDate timeIntervalSinceReferenceDate] - _startExecutionTime > MAX_EXECUTION_TIME)) { + [self performSelector:@selector(executePending) withObject:nil afterDelay:0]; + return; + } + } + } + } @finally + { + _startExecutionTime = 0; + } +} + +- (BOOL)execute:(CDVInvokedUrlCommand*)command +{ + if ((command.className == nil) || (command.methodName == nil)) { + NSLog(@"ERROR: Classname and/or methodName not found for command."); + return NO; + } + + // Fetch an instance of this class + CDVPlugin* obj = [_viewController.commandDelegate getCommandInstance:command.className]; + + if (!([obj isKindOfClass:[CDVPlugin class]])) { + NSLog(@"ERROR: Plugin '%@' not found, or is not a CDVPlugin. Check your plugin mapping in config.xml.", command.className); + return NO; + } + BOOL retVal = YES; + double started = [[NSDate date] timeIntervalSince1970] * 1000.0; + // Find the proper selector to call. + NSString* methodName = [NSString stringWithFormat:@"%@:", command.methodName]; + SEL normalSelector = NSSelectorFromString(methodName); + if ([obj respondsToSelector:normalSelector]) { + // [obj performSelector:normalSelector withObject:command]; + ((void (*)(id, SEL, id))objc_msgSend)(obj, normalSelector, command); + } else { + // There's no method to call, so throw an error. + NSLog(@"ERROR: Method '%@' not defined in Plugin '%@'", methodName, command.className); + retVal = NO; + } + double elapsed = [[NSDate date] timeIntervalSince1970] * 1000.0 - started; + if (elapsed > 10) { + NSLog(@"THREAD WARNING: ['%@'] took '%f' ms. Plugin should use a background thread.", command.className, elapsed); + } + return retVal; +} + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVConfigParser.m b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVConfigParser.m new file mode 100644 index 00000000..a5663e59 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVConfigParser.m @@ -0,0 +1,81 @@ +/* + 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. + */ + +#import + +@interface CDVConfigParser () + +@property (nonatomic, readwrite, strong) NSMutableDictionary* pluginsDict; +@property (nonatomic, readwrite, strong) NSMutableDictionary* settings; +@property (nonatomic, readwrite, strong) NSMutableArray* startupPluginNames; +@property (nonatomic, readwrite, strong) NSString* startPage; + +@end + +@implementation CDVConfigParser + +@synthesize pluginsDict, settings, startPage, startupPluginNames; + +- (id)init +{ + self = [super init]; + if (self != nil) { + self.pluginsDict = [[NSMutableDictionary alloc] initWithCapacity:30]; + self.settings = [[NSMutableDictionary alloc] initWithCapacity:30]; + self.startupPluginNames = [[NSMutableArray alloc] initWithCapacity:8]; + featureName = nil; + } + return self; +} + +- (void)parser:(NSXMLParser*)parser didStartElement:(NSString*)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qualifiedName attributes:(NSDictionary*)attributeDict +{ + if ([elementName isEqualToString:@"preference"]) { + settings[[attributeDict[@"name"] lowercaseString]] = attributeDict[@"value"]; + } else if ([elementName isEqualToString:@"feature"]) { // store feature name to use with correct parameter set + featureName = [attributeDict[@"name"] lowercaseString]; + } else if ((featureName != nil) && [elementName isEqualToString:@"param"]) { + NSString* paramName = [attributeDict[@"name"] lowercaseString]; + id value = attributeDict[@"value"]; + if ([paramName isEqualToString:@"ios-package"]) { + pluginsDict[featureName] = value; + } + BOOL paramIsOnload = ([paramName isEqualToString:@"onload"] && [@"true" isEqualToString : value]); + BOOL attribIsOnload = [@"true" isEqualToString :[attributeDict[@"onload"] lowercaseString]]; + if (paramIsOnload || attribIsOnload) { + [self.startupPluginNames addObject:featureName]; + } + } else if ([elementName isEqualToString:@"content"]) { + self.startPage = attributeDict[@"src"]; + } +} + +- (void)parser:(NSXMLParser*)parser didEndElement:(NSString*)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qualifiedName +{ + if ([elementName isEqualToString:@"feature"]) { // no longer handling a feature so release + featureName = nil; + } +} + +- (void)parser:(NSXMLParser*)parser parseErrorOccurred:(NSError*)parseError +{ + NSAssert(NO, @"config.xml parse error line %ld col %ld", (long)[parser lineNumber], (long)[parser columnNumber]); +} + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVInvokedUrlCommand.m b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVInvokedUrlCommand.m new file mode 100644 index 00000000..c0e6b79f --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVInvokedUrlCommand.m @@ -0,0 +1,116 @@ +/* + 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. + */ + +#import +#import "CDVJSON_private.h" + +@implementation CDVInvokedUrlCommand + +@synthesize arguments = _arguments; +@synthesize callbackId = _callbackId; +@synthesize className = _className; +@synthesize methodName = _methodName; + ++ (CDVInvokedUrlCommand*)commandFromJson:(NSArray*)jsonEntry +{ + return [[CDVInvokedUrlCommand alloc] initFromJson:jsonEntry]; +} + +- (id)initFromJson:(NSArray*)jsonEntry +{ + id tmp = [jsonEntry objectAtIndex:0]; + NSString* callbackId = tmp == [NSNull null] ? nil : tmp; + NSString* className = [jsonEntry objectAtIndex:1]; + NSString* methodName = [jsonEntry objectAtIndex:2]; + NSMutableArray* arguments = [jsonEntry objectAtIndex:3]; + + return [self initWithArguments:arguments + callbackId:callbackId + className:className + methodName:methodName]; +} + +- (id)initWithArguments:(NSArray*)arguments + callbackId:(NSString*)callbackId + className:(NSString*)className + methodName:(NSString*)methodName +{ + self = [super init]; + if (self != nil) { + _arguments = arguments; + _callbackId = callbackId; + _className = className; + _methodName = methodName; + } + [self massageArguments]; + return self; +} + +- (void)massageArguments +{ + NSMutableArray* newArgs = nil; + + for (NSUInteger i = 0, count = [_arguments count]; i < count; ++i) { + id arg = [_arguments objectAtIndex:i]; + if (![arg isKindOfClass:[NSDictionary class]]) { + continue; + } + NSDictionary* dict = arg; + NSString* type = [dict objectForKey:@"CDVType"]; + if (!type || ![type isEqualToString:@"ArrayBuffer"]) { + continue; + } + NSString* data = [dict objectForKey:@"data"]; + if (!data) { + continue; + } + if (newArgs == nil) { + newArgs = [NSMutableArray arrayWithArray:_arguments]; + _arguments = newArgs; + } + [newArgs replaceObjectAtIndex:i withObject:[[NSData alloc] initWithBase64EncodedString:data options:0]]; + } +} + +- (id)argumentAtIndex:(NSUInteger)index +{ + return [self argumentAtIndex:index withDefault:nil]; +} + +- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue +{ + return [self argumentAtIndex:index withDefault:defaultValue andClass:nil]; +} + +- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue andClass:(Class)aClass +{ + if (index >= [_arguments count]) { + return defaultValue; + } + id ret = [_arguments objectAtIndex:index]; + if (ret == [NSNull null]) { + ret = defaultValue; + } + if ((aClass != nil) && ![ret isKindOfClass:aClass]) { + ret = defaultValue; + } + return ret; +} + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVPlugin+Resources.m b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVPlugin+Resources.m new file mode 100644 index 00000000..f18f7429 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVPlugin+Resources.m @@ -0,0 +1,38 @@ +/* + 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. + */ + +#import + +@implementation CDVPlugin (CDVPluginResources) + +- (NSString*)pluginLocalizedString:(NSString*)key +{ + NSBundle* bundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:NSStringFromClass([self class]) ofType:@"bundle"]]; + + return [bundle localizedStringForKey:(key) value:nil table:nil]; +} + +- (UIImage*)pluginImageResource:(NSString*)name +{ + NSString* resourceIdentifier = [NSString stringWithFormat:@"%@.bundle/%@", NSStringFromClass([self class]), name]; + + return [UIImage imageNamed:resourceIdentifier]; +} + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVPlugin.m b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVPlugin.m new file mode 100644 index 00000000..275f52e9 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVPlugin.m @@ -0,0 +1,199 @@ +/* + 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. + */ + +#import +#import "CDVPlugin+Private.h" +#import +#import +#include + +@implementation UIView (org_apache_cordova_UIView_Extension) + +@dynamic scrollView; + +- (UIScrollView*)scrollView +{ + SEL scrollViewSelector = NSSelectorFromString(@"scrollView"); + + if ([self respondsToSelector:scrollViewSelector]) { + return ((id (*)(id, SEL))objc_msgSend)(self, scrollViewSelector); + } + + return nil; +} + +@end + +NSString* const CDVPageDidLoadNotification = @"CDVPageDidLoadNotification"; +NSString* const CDVPluginHandleOpenURLNotification = @"CDVPluginHandleOpenURLNotification"; +NSString* const CDVPluginHandleOpenURLWithAppSourceAndAnnotationNotification = @"CDVPluginHandleOpenURLWithAppSourceAndAnnotationNotification"; +NSString* const CDVPluginResetNotification = @"CDVPluginResetNotification"; +NSString* const CDVViewWillAppearNotification = @"CDVViewWillAppearNotification"; +NSString* const CDVViewDidAppearNotification = @"CDVViewDidAppearNotification"; +NSString* const CDVViewWillDisappearNotification = @"CDVViewWillDisappearNotification"; +NSString* const CDVViewDidDisappearNotification = @"CDVViewDidDisappearNotification"; +NSString* const CDVViewWillLayoutSubviewsNotification = @"CDVViewWillLayoutSubviewsNotification"; +NSString* const CDVViewDidLayoutSubviewsNotification = @"CDVViewDidLayoutSubviewsNotification"; +NSString* const CDVViewWillTransitionToSizeNotification = @"CDVViewWillTransitionToSizeNotification"; + +@interface CDVPlugin () + +@property (readwrite, assign) BOOL hasPendingOperation; +@property (nonatomic, readwrite, weak) id webViewEngine; + +@end + +@implementation CDVPlugin +@synthesize webViewEngine, viewController, commandDelegate, hasPendingOperation; +@dynamic webView; + +// Do not override these methods. Use pluginInitialize instead. +- (instancetype)initWithWebViewEngine:(id )theWebViewEngine +{ + self = [self init]; + if (self) { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppTerminate) name:UIApplicationWillTerminateNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOpenURL:) name:CDVPluginHandleOpenURLNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOpenURLWithApplicationSourceAndAnnotation:) name:CDVPluginHandleOpenURLWithAppSourceAndAnnotationNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onReset) name:CDVPluginResetNotification object:theWebViewEngine.engineWebView]; + + self.webViewEngine = theWebViewEngine; + } + return self; +} + +- (void)pluginInitialize +{ + // You can listen to more app notifications, see: + // http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIApplication_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006728-CH3-DontLinkElementID_4 + + // NOTE: if you want to use these, make sure you uncomment the corresponding notification handler + + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onPause) name:UIApplicationDidEnterBackgroundNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResume) name:UIApplicationWillEnterForegroundNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationWillChange) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationDidChange) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; + + // Added in 2.5.0 + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad:) name:CDVPageDidLoadNotification object:self.webView]; + //Added in 4.3.0 + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillAppear:) name:CDVViewWillAppearNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidAppear:) name:CDVViewDidAppearNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillDisappear:) name:CDVViewWillDisappearNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidDisappear:) name:CDVViewDidDisappearNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillLayoutSubviews:) name:CDVViewWillLayoutSubviewsNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidLayoutSubviews:) name:CDVViewDidLayoutSubviewsNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillTransitionToSize:) name:CDVViewWillTransitionToSizeNotification object:nil]; +} + +- (void)dispose +{ + viewController = nil; + commandDelegate = nil; +} + +- (UIView*)webView +{ + if (self.webViewEngine != nil) { + return self.webViewEngine.engineWebView; + } + + return nil; +} + +/* +// NOTE: for onPause and onResume, calls into JavaScript must not call or trigger any blocking UI, like alerts +- (void) onPause {} +- (void) onResume {} +- (void) onOrientationWillChange {} +- (void) onOrientationDidChange {} +*/ + +/* NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts */ +- (void)handleOpenURL:(NSNotification*)notification +{ + // override to handle urls sent to your app + // register your url schemes in your App-Info.plist + + NSURL* url = [notification object]; + + if ([url isKindOfClass:[NSURL class]]) { + /* Do your thing! */ + } +} + +/* + NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts + */ +- (void)handleOpenURLWithApplicationSourceAndAnnotation: (NSNotification*)notification +{ + + // override to handle urls sent to your app + // register your url schemes in your App-Info.plist + + // The notification object is an NSDictionary which contains + // - url which is a type of NSURL + // - sourceApplication which is a type of NSString and represents the package + // id of the app that calls our app + // - annotation which a type of Property list which can be several different types + // please see https://developer.apple.com/library/content/documentation/General/Conceptual/DevPedia-CocoaCore/PropertyList.html + + NSDictionary* notificationData = [notification object]; + + if ([notificationData isKindOfClass: NSDictionary.class]){ + + NSURL* url = notificationData[@"url"]; + NSString* sourceApplication = notificationData[@"sourceApplication"]; + id annotation = notificationData[@"annotation"]; + + if ([url isKindOfClass:NSURL.class] && [sourceApplication isKindOfClass:NSString.class] && annotation) { + /* Do your thing! */ + } + } +} + + +/* NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts */ +- (void)onAppTerminate +{ + // override this if you need to do any cleanup on app exit +} + +- (void)onMemoryWarning +{ + // override to remove caches, etc +} + +- (void)onReset +{ + // Override to cancel any long-running requests when the WebView navigates or refreshes. +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; // this will remove all notifications unless added using addObserverForName:object:queue:usingBlock: +} + +- (id)appDelegate +{ + return [[UIApplication sharedApplication] delegate]; +} + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVPluginResult.m b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVPluginResult.m new file mode 100644 index 00000000..1a5ab024 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVPluginResult.m @@ -0,0 +1,203 @@ +/* + 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. + */ + +#import +#import "CDVJSON_private.h" +#import "CDVDebug.h" + +// This exists to preserve compatibility with early Swift plugins, who are +// using CDVCommandStatus as ObjC-style constants rather than as Swift enum +// values. +// These constants alias the enum values back to their previous names. +#define SWIFT_ENUM_COMPAT_HACK(enumVal) const CDVCommandStatus SWIFT_##enumVal NS_SWIFT_NAME(enumVal) = enumVal +SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_NO_RESULT); +SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_OK); +SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_CLASS_NOT_FOUND_EXCEPTION); +SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_ILLEGAL_ACCESS_EXCEPTION); +SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_INSTANTIATION_EXCEPTION); +SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_MALFORMED_URL_EXCEPTION); +SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_IO_EXCEPTION); +SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_INVALID_ACTION); +SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_JSON_EXCEPTION); +SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_ERROR); +#undef SWIFT_ENUM_COMPAT_HACK + +@interface CDVPluginResult () + +- (CDVPluginResult*)initWithStatus:(CDVCommandStatus)statusOrdinal message:(id)theMessage; + +@end + +@implementation CDVPluginResult +@synthesize status, message, keepCallback, associatedObject; + +static NSArray* org_apache_cordova_CommandStatusMsgs; + +id messageFromArrayBuffer(NSData* data) +{ + return @{ + @"CDVType" : @"ArrayBuffer", + @"data" :[data base64EncodedStringWithOptions:0] + }; +} + +id massageMessage(id message) +{ + if ([message isKindOfClass:[NSData class]]) { + return messageFromArrayBuffer(message); + } + return message; +} + +id messageFromMultipart(NSArray* theMessages) +{ + NSMutableArray* messages = [NSMutableArray arrayWithArray:theMessages]; + + for (NSUInteger i = 0; i < messages.count; ++i) { + [messages replaceObjectAtIndex:i withObject:massageMessage([messages objectAtIndex:i])]; + } + + return @{ + @"CDVType" : @"MultiPart", + @"messages" : messages + }; +} + ++ (void)initialize +{ + org_apache_cordova_CommandStatusMsgs = [[NSArray alloc] initWithObjects:@"No result", + @"OK", + @"Class not found", + @"Illegal access", + @"Instantiation error", + @"Malformed url", + @"IO error", + @"Invalid action", + @"JSON error", + @"Error", + nil]; +} + +- (CDVPluginResult*)init +{ + return [self initWithStatus:CDVCommandStatus_NO_RESULT message:nil]; +} + +- (CDVPluginResult*)initWithStatus:(CDVCommandStatus)statusOrdinal message:(id)theMessage +{ + self = [super init]; + if (self) { + status = @(statusOrdinal); + message = theMessage; + keepCallback = [NSNumber numberWithBool:NO]; + } + return self; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal +{ + return [[self alloc] initWithStatus:statusOrdinal message:nil]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsString:(NSString*)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:theMessage]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArray:(NSArray*)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:theMessage]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsInt:(int)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithInt:theMessage]]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsNSInteger:(NSInteger)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithInteger:theMessage]]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsNSUInteger:(NSUInteger)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithUnsignedInteger:theMessage]]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDouble:(double)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithDouble:theMessage]]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsBool:(BOOL)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithBool:theMessage]]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDictionary:(NSDictionary*)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:theMessage]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:messageFromArrayBuffer(theMessage)]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsMultipart:(NSArray*)theMessages +{ + return [[self alloc] initWithStatus:statusOrdinal message:messageFromMultipart(theMessages)]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode +{ + NSDictionary* errDict = @{@"code" :[NSNumber numberWithInt:errorCode]}; + + return [[self alloc] initWithStatus:statusOrdinal message:errDict]; +} + +- (void)setKeepCallbackAsBool:(BOOL)bKeepCallback +{ + [self setKeepCallback:[NSNumber numberWithBool:bKeepCallback]]; +} + +- (NSString*)argumentsAsJSON +{ + id arguments = (self.message == nil ? [NSNull null] : self.message); + NSArray* argumentsWrappedInArray = [NSArray arrayWithObject:arguments]; + + NSString* argumentsJSON = [argumentsWrappedInArray cdv_JSONString]; + + argumentsJSON = [argumentsJSON substringWithRange:NSMakeRange(1, [argumentsJSON length] - 2)]; + + return argumentsJSON; +} + +static BOOL gIsVerbose = NO; ++ (void)setVerbose:(BOOL)verbose +{ + gIsVerbose = verbose; +} + ++ (BOOL)isVerbose +{ + return gIsVerbose; +} + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVTimer.m b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVTimer.m new file mode 100644 index 00000000..1d62f952 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVTimer.m @@ -0,0 +1,123 @@ +/* + 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. + */ + +#import + +#pragma mark CDVTimerItem + +@interface CDVTimerItem : NSObject + +@property (nonatomic, strong) NSString* name; +@property (nonatomic, strong) NSDate* started; +@property (nonatomic, strong) NSDate* ended; + +- (void)log; + +@end + +@implementation CDVTimerItem + +- (void)log +{ + NSLog(@"[CDVTimer][%@] %fms", self.name, [self.ended timeIntervalSinceDate:self.started] * 1000.0); +} + +@end + +#pragma mark CDVTimer + +@interface CDVTimer () + +@property (nonatomic, strong) NSMutableDictionary* items; + +@end + +@implementation CDVTimer + +#pragma mark object methods + +- (id)init +{ + if (self = [super init]) { + self.items = [NSMutableDictionary dictionaryWithCapacity:6]; + } + + return self; +} + +- (void)add:(NSString*)name +{ + if ([self.items objectForKey:[name lowercaseString]] == nil) { + CDVTimerItem* item = [CDVTimerItem new]; + item.name = name; + item.started = [NSDate new]; + [self.items setObject:item forKey:[name lowercaseString]]; + } else { + NSLog(@"Timer called '%@' already exists.", name); + } +} + +- (void)remove:(NSString*)name +{ + CDVTimerItem* item = [self.items objectForKey:[name lowercaseString]]; + + if (item != nil) { + item.ended = [NSDate new]; + [item log]; + [self.items removeObjectForKey:[name lowercaseString]]; + } else { + NSLog(@"Timer called '%@' does not exist.", name); + } +} + +- (void)removeAll +{ + [self.items removeAllObjects]; +} + +#pragma mark class methods + ++ (void)start:(NSString*)name +{ + [[CDVTimer sharedInstance] add:name]; +} + ++ (void)stop:(NSString*)name +{ + [[CDVTimer sharedInstance] remove:name]; +} + ++ (void)clearAll +{ + [[CDVTimer sharedInstance] removeAll]; +} + ++ (CDVTimer*)sharedInstance +{ + static dispatch_once_t pred = 0; + __strong static CDVTimer* _sharedObject = nil; + + dispatch_once(&pred, ^{ + _sharedObject = [[self alloc] init]; + }); + + return _sharedObject; +} + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVURLSchemeHandler.m b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVURLSchemeHandler.m new file mode 100644 index 00000000..ec9fcb1c --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVURLSchemeHandler.m @@ -0,0 +1,137 @@ +/* + 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. + */ + + +#import +#import + +#import + +@implementation CDVURLSchemeHandler + + +- (instancetype)initWithVC:(CDVViewController *)controller +{ + self = [super init]; + if (self) { + _viewController = controller; + } + return self; +} + +- (void)webView:(WKWebView *)webView startURLSchemeTask:(id )urlSchemeTask +{ + NSString * startPath = [[NSBundle mainBundle] pathForResource:self.viewController.wwwFolderName ofType: nil]; + NSURL * url = urlSchemeTask.request.URL; + NSString * stringToLoad = url.path; + NSString * scheme = url.scheme; + + CDVViewController* vc = (CDVViewController*)self.viewController; + + /* + * Give plugins the chance to handle the url + */ + BOOL anyPluginsResponded = NO; + BOOL handledRequest = NO; + + NSDictionary *pluginObjects = [[vc pluginObjects] copy]; + for (NSString* pluginName in pluginObjects) { + self.schemePlugin = [vc.pluginObjects objectForKey:pluginName]; + SEL selector = NSSelectorFromString(@"overrideSchemeTask:"); + if ([self.schemePlugin respondsToSelector:selector]) { + handledRequest = (((BOOL (*)(id, SEL, id ))objc_msgSend)(self.schemePlugin, selector, urlSchemeTask)); + if (handledRequest) { + anyPluginsResponded = YES; + break; + } + } + } + + if (!anyPluginsResponded) { + if ([scheme isEqualToString:self.viewController.appScheme]) { + if ([stringToLoad hasPrefix:@"/_app_file_"]) { + startPath = [stringToLoad stringByReplacingOccurrencesOfString:@"/_app_file_" withString:@""]; + } else { + if ([stringToLoad isEqualToString:@""] || [url.pathExtension isEqualToString:@""]) { + startPath = [startPath stringByAppendingPathComponent:self.viewController.startPage]; + } else { + startPath = [startPath stringByAppendingPathComponent:stringToLoad]; + } + } + } + + NSError * fileError = nil; + NSData * data = nil; + if ([self isMediaExtension:url.pathExtension]) { + data = [NSData dataWithContentsOfFile:startPath options:NSDataReadingMappedIfSafe error:&fileError]; + } + if (!data || fileError) { + data = [[NSData alloc] initWithContentsOfFile:startPath]; + } + NSInteger statusCode = 200; + if (!data) { + statusCode = 404; + } + NSURL * localUrl = [NSURL URLWithString:url.absoluteString]; + NSString * mimeType = [self getMimeType:url.pathExtension]; + id response = nil; + if (data && [self isMediaExtension:url.pathExtension]) { + response = [[NSURLResponse alloc] initWithURL:localUrl MIMEType:mimeType expectedContentLength:data.length textEncodingName:nil]; + } else { + NSDictionary * headers = @{ @"Content-Type" : mimeType, @"Cache-Control": @"no-cache"}; + response = [[NSHTTPURLResponse alloc] initWithURL:localUrl statusCode:statusCode HTTPVersion:nil headerFields:headers]; + } + + [urlSchemeTask didReceiveResponse:response]; + if (data) { + [urlSchemeTask didReceiveData:data]; + } + [urlSchemeTask didFinish]; + } +} + +- (void)webView:(nonnull WKWebView *)webView stopURLSchemeTask:(nonnull id)urlSchemeTask +{ + SEL selector = NSSelectorFromString(@"stopSchemeTask:"); + if (self.schemePlugin != nil && [self.schemePlugin respondsToSelector:selector]) { + (((void (*)(id, SEL, id ))objc_msgSend)(self.schemePlugin, selector, urlSchemeTask)); + } +} + +-(NSString *) getMimeType:(NSString *)fileExtension { + if (fileExtension && ![fileExtension isEqualToString:@""]) { + NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)fileExtension, NULL); + NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType); + return contentType ? contentType : @"application/octet-stream"; + } else { + return @"text/html"; + } +} + +-(BOOL) isMediaExtension:(NSString *) pathExtension { + NSArray * mediaExtensions = @[@"m4v", @"mov", @"mp4", + @"aac", @"ac3", @"aiff", @"au", @"flac", @"m4a", @"mp3", @"wav"]; + if ([mediaExtensions containsObject:pathExtension.lowercaseString]) { + return YES; + } + return NO; +} + + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVViewController.m b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVViewController.m new file mode 100644 index 00000000..dd8ba165 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVViewController.m @@ -0,0 +1,780 @@ +/* + 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. + */ + +@import AVFoundation; +@import Foundation; +@import WebKit; + +#import +#import +#import +#import "CDVPlugin+Private.h" +#import +#import +#import "CDVCommandDelegateImpl.h" + +@interface CDVViewController () { } + +@property (nonatomic, readwrite, strong) NSXMLParser* configParser; +@property (nonatomic, readwrite, strong) NSMutableDictionary* settings; +@property (nonatomic, readwrite, strong) NSMutableDictionary* pluginObjects; +@property (nonatomic, readwrite, strong) NSMutableArray* startupPluginNames; +@property (nonatomic, readwrite, strong) NSDictionary* pluginsMap; +@property (nonatomic, readwrite, strong) id webViewEngine; +@property (nonatomic, readwrite, strong) UIView* launchView; + +@property (readwrite, assign) BOOL initialized; + +@property (atomic, strong) NSURL* openURL; + +@end + +@implementation CDVViewController + +@synthesize supportedOrientations; +@synthesize pluginObjects, pluginsMap, startupPluginNames; +@synthesize configParser, settings; +@synthesize wwwFolderName, startPage, initialized, openURL; +@synthesize commandDelegate = _commandDelegate; +@synthesize commandQueue = _commandQueue; +@synthesize webViewEngine = _webViewEngine; +@dynamic webView; + +- (void)__init +{ + if ((self != nil) && !self.initialized) { + _commandQueue = [[CDVCommandQueue alloc] initWithViewController:self]; + _commandDelegate = [[CDVCommandDelegateImpl alloc] initWithViewController:self]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillTerminate:) + name:UIApplicationWillTerminateNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillResignActive:) + name:UIApplicationWillResignActiveNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidBecomeActive:) + name:UIApplicationDidBecomeActiveNotification object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillEnterForeground:) + name:UIApplicationWillEnterForegroundNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidEnterBackground:) + name:UIApplicationDidEnterBackgroundNotification object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onWebViewPageDidLoad:) + name:CDVPageDidLoadNotification object:nil]; + + // read from UISupportedInterfaceOrientations (or UISupportedInterfaceOrientations~iPad, if its iPad) from -Info.plist + self.supportedOrientations = [self parseInterfaceOrientations: + [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UISupportedInterfaceOrientations"]]; + + self.initialized = YES; + } +} + +- (id)initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + [self __init]; + return self; +} + +- (id)initWithCoder:(NSCoder*)aDecoder +{ + self = [super initWithCoder:aDecoder]; + [self __init]; + return self; +} + +- (id)init +{ + self = [super init]; + [self __init]; + return self; +} + +-(NSString*)configFilePath{ + NSString* path = self.configFile ?: @"config.xml"; + + // if path is relative, resolve it against the main bundle + if(![path isAbsolutePath]){ + NSString* absolutePath = [[NSBundle mainBundle] pathForResource:path ofType:nil]; + if(!absolutePath){ + NSAssert(NO, @"ERROR: %@ not found in the main bundle!", path); + } + path = absolutePath; + } + + // Assert file exists + if (![[NSFileManager defaultManager] fileExistsAtPath:path]) { + NSAssert(NO, @"ERROR: %@ does not exist. Please run cordova-ios/bin/cordova_plist_to_config_xml path/to/project.", path); + return nil; + } + + return path; +} + +- (void)parseSettingsWithParser:(NSObject *)delegate +{ + // read from config.xml in the app bundle + NSString* path = [self configFilePath]; + + NSURL* url = [NSURL fileURLWithPath:path]; + + self.configParser = [[NSXMLParser alloc] initWithContentsOfURL:url]; + if (self.configParser == nil) { + NSLog(@"Failed to initialize XML parser."); + return; + } + [self.configParser setDelegate:((id < NSXMLParserDelegate >)delegate)]; + [self.configParser parse]; +} + +- (void)loadSettings +{ + CDVConfigParser* delegate = [[CDVConfigParser alloc] init]; + + [self parseSettingsWithParser:delegate]; + + // Get the plugin dictionary, allowList and settings from the delegate. + self.pluginsMap = delegate.pluginsDict; + self.startupPluginNames = delegate.startupPluginNames; + self.settings = delegate.settings; + + // And the start folder/page. + if(self.wwwFolderName == nil){ + self.wwwFolderName = @"www"; + } + if(delegate.startPage && self.startPage == nil){ + self.startPage = delegate.startPage; + } + if (self.startPage == nil) { + self.startPage = @"index.html"; + } + + // Initialize the plugin objects dict. + self.pluginObjects = [[NSMutableDictionary alloc] initWithCapacity:20]; +} + +- (NSURL*)appUrl +{ + NSURL* appURL = nil; + + if ([self.startPage rangeOfString:@"://"].location != NSNotFound) { + appURL = [NSURL URLWithString:self.startPage]; + } else if ([self.wwwFolderName rangeOfString:@"://"].location != NSNotFound) { + appURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/%@", self.wwwFolderName, self.startPage]]; + } else if([self.wwwFolderName rangeOfString:@".bundle"].location != NSNotFound){ + // www folder is actually a bundle + NSBundle* bundle = [NSBundle bundleWithPath:self.wwwFolderName]; + appURL = [bundle URLForResource:self.startPage withExtension:nil]; + } else if([self.wwwFolderName rangeOfString:@".framework"].location != NSNotFound){ + // www folder is actually a framework + NSBundle* bundle = [NSBundle bundleWithPath:self.wwwFolderName]; + appURL = [bundle URLForResource:self.startPage withExtension:nil]; + } else { + // CB-3005 strip parameters from start page to check if page exists in resources + NSURL* startURL = [NSURL URLWithString:self.startPage]; + NSString* startFilePath = [self.commandDelegate pathForResource:[startURL path]]; + + if (startFilePath == nil) { + appURL = nil; + } else { + appURL = [NSURL fileURLWithPath:startFilePath]; + // CB-3005 Add on the query params or fragment. + NSString* startPageNoParentDirs = self.startPage; + NSRange r = [startPageNoParentDirs rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"?#"] options:0]; + if (r.location != NSNotFound) { + NSString* queryAndOrFragment = [self.startPage substringFromIndex:r.location]; + appURL = [NSURL URLWithString:queryAndOrFragment relativeToURL:appURL]; + } + } + } + + return appURL; +} + +- (nullable NSURL*)errorURL +{ + NSURL* errorUrl = nil; + + id setting = [self.settings cordovaSettingForKey:@"ErrorUrl"]; + + if (setting) { + NSString* errorUrlString = (NSString*)setting; + if ([errorUrlString rangeOfString:@"://"].location != NSNotFound) { + errorUrl = [NSURL URLWithString:errorUrlString]; + } else { + NSURL* url = [NSURL URLWithString:(NSString*)setting]; + NSString* errorFilePath = [self.commandDelegate pathForResource:[url path]]; + if (errorFilePath) { + errorUrl = [NSURL fileURLWithPath:errorFilePath]; + } + } + } + + return errorUrl; +} + +- (UIView*)webView +{ + if (self.webViewEngine != nil) { + return self.webViewEngine.engineWebView; + } + + return nil; +} + +// Implement viewDidLoad to do additional setup after loading the view, typically from a nib. +- (void)viewDidLoad +{ + [super viewDidLoad]; + + // Load settings + [self loadSettings]; + + // // Instantiate the Launch screen ///////// + + if (!self.launchView) { + [self createLaunchView]; + } + + // // Instantiate the WebView /////////////// + + if (!self.webView) { + [self createGapView]; + } + + // ///////////////// + + if ([self.startupPluginNames count] > 0) { + [CDVTimer start:@"TotalPluginStartup"]; + + for (NSString* pluginName in self.startupPluginNames) { + [CDVTimer start:pluginName]; + [self getCommandInstance:pluginName]; + [CDVTimer stop:pluginName]; + } + + [CDVTimer stop:@"TotalPluginStartup"]; + } + + // ///////////////// + NSURL* appURL = [self appUrl]; + + if (appURL) { + NSURLRequest* appReq = [NSURLRequest requestWithURL:appURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0]; + [self.webViewEngine loadRequest:appReq]; + } else { + NSString* loadErr = [NSString stringWithFormat:@"ERROR: Start Page at '%@/%@' was not found.", self.wwwFolderName, self.startPage]; + NSLog(@"%@", loadErr); + + NSURL* errorUrl = [self errorURL]; + if (errorUrl) { + errorUrl = [NSURL URLWithString:[NSString stringWithFormat:@"?error=%@", [loadErr stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLPathAllowedCharacterSet]] relativeToURL:errorUrl]; + NSLog(@"%@", [errorUrl absoluteString]); + [self.webViewEngine loadRequest:[NSURLRequest requestWithURL:errorUrl]]; + } else { + NSString* html = [NSString stringWithFormat:@" %@ ", loadErr]; + [self.webViewEngine loadHTMLString:html baseURL:nil]; + } + } + // ///////////////// + + UIColor* bgColor = [UIColor colorNamed:@"BackgroundColor"] ?: UIColor.whiteColor; + [self.launchView setBackgroundColor:bgColor]; + [self.webView setBackgroundColor:bgColor]; +} + +-(void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVViewWillAppearNotification object:nil]]; +} + +-(void)viewDidAppear:(BOOL)animated +{ + [super viewDidAppear:animated]; + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVViewDidAppearNotification object:nil]]; +} + +-(void)viewWillDisappear:(BOOL)animated +{ + [super viewWillDisappear:animated]; + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVViewWillDisappearNotification object:nil]]; +} + +-(void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear:animated]; + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVViewDidDisappearNotification object:nil]]; +} + +-(void)viewWillLayoutSubviews +{ + [super viewWillLayoutSubviews]; + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVViewWillLayoutSubviewsNotification object:nil]]; +} + +-(void)viewDidLayoutSubviews +{ + [super viewDidLayoutSubviews]; + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVViewDidLayoutSubviewsNotification object:nil]]; +} + +-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator +{ + [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVViewWillTransitionToSizeNotification object:[NSValue valueWithCGSize:size]]]; +} + +- (NSArray*)parseInterfaceOrientations:(NSArray*)orientations +{ + NSMutableArray* result = [[NSMutableArray alloc] init]; + + if (orientations != nil) { + NSEnumerator* enumerator = [orientations objectEnumerator]; + NSString* orientationString; + + while (orientationString = [enumerator nextObject]) { + if ([orientationString isEqualToString:@"UIInterfaceOrientationPortrait"]) { + [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortrait]]; + } else if ([orientationString isEqualToString:@"UIInterfaceOrientationPortraitUpsideDown"]) { + [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortraitUpsideDown]]; + } else if ([orientationString isEqualToString:@"UIInterfaceOrientationLandscapeLeft"]) { + [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft]]; + } else if ([orientationString isEqualToString:@"UIInterfaceOrientationLandscapeRight"]) { + [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight]]; + } + } + } + + // default + if ([result count] == 0) { + [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortrait]]; + } + + return result; +} + +- (BOOL)shouldAutorotate +{ + return YES; +} + +- (UIInterfaceOrientationMask)supportedInterfaceOrientations +{ + NSUInteger ret = 0; + + if ([self supportsOrientation:UIInterfaceOrientationPortrait]) { + ret = ret | (1 << UIInterfaceOrientationPortrait); + } + if ([self supportsOrientation:UIInterfaceOrientationPortraitUpsideDown]) { + ret = ret | (1 << UIInterfaceOrientationPortraitUpsideDown); + } + if ([self supportsOrientation:UIInterfaceOrientationLandscapeRight]) { + ret = ret | (1 << UIInterfaceOrientationLandscapeRight); + } + if ([self supportsOrientation:UIInterfaceOrientationLandscapeLeft]) { + ret = ret | (1 << UIInterfaceOrientationLandscapeLeft); + } + + return ret; +} + +- (BOOL)supportsOrientation:(UIInterfaceOrientation)orientation +{ + return [self.supportedOrientations containsObject:@(orientation)]; +} + +/// Retrieves the view from a newwly initialized webViewEngine +/// @param bounds The bounds with which the webViewEngine will be initialized +- (nonnull UIView*)newCordovaViewWithFrame:(CGRect)bounds +{ + NSString* defaultWebViewEngineClassName = [self.settings cordovaSettingForKey:@"CordovaDefaultWebViewEngine"]; + NSString* webViewEngineClassName = [self.settings cordovaSettingForKey:@"CordovaWebViewEngine"]; + + if (!defaultWebViewEngineClassName) { + defaultWebViewEngineClassName = @"CDVWebViewEngine"; + } + if (!webViewEngineClassName) { + webViewEngineClassName = defaultWebViewEngineClassName; + } + + // Determine if a provided custom web view engine is sufficient + id engine; + Class customWebViewEngineClass = NSClassFromString(webViewEngineClassName); + if (customWebViewEngineClass) { + id customWebViewEngine = [self initWebViewEngine:customWebViewEngineClass bounds:bounds]; + BOOL customConformsToProtocol = [customWebViewEngine conformsToProtocol:@protocol(CDVWebViewEngineProtocol)]; + BOOL customCanLoad = [customWebViewEngine canLoadRequest:[NSURLRequest requestWithURL:self.appUrl]]; + if (customConformsToProtocol && customCanLoad) { + engine = customWebViewEngine; + } + } + + // Otherwise use the default web view engine + if (!engine) { + Class defaultWebViewEngineClass = NSClassFromString(defaultWebViewEngineClassName); + id defaultWebViewEngine = [self initWebViewEngine:defaultWebViewEngineClass bounds:bounds]; + NSAssert([defaultWebViewEngine conformsToProtocol:@protocol(CDVWebViewEngineProtocol)], + @"we expected the default web view engine to conform to the CDVWebViewEngineProtocol"); + engine = defaultWebViewEngine; + } + + if ([engine isKindOfClass:[CDVPlugin class]]) { + [self registerPlugin:(CDVPlugin*)engine withClassName:webViewEngineClassName]; + } + + self.webViewEngine = engine; + return self.webViewEngine.engineWebView; +} + +/// Initialiizes the webViewEngine, with config, if supported and provided +/// @param engineClass A class that must conform to the `CDVWebViewEngineProtocol` +/// @param bounds with which the webview will be initialized +- (id _Nullable) initWebViewEngine:(nonnull Class)engineClass bounds:(CGRect)bounds { + WKWebViewConfiguration *config = [self respondsToSelector:@selector(configuration)] ? [self configuration] : nil; + if (config && [engineClass respondsToSelector:@selector(initWithFrame:configuration:)]) { + return [[engineClass alloc] initWithFrame:bounds configuration:config]; + } else { + return [[engineClass alloc] initWithFrame:bounds]; + } +} + +- (void)createLaunchView +{ + CGRect webViewBounds = self.view.bounds; + webViewBounds.origin = self.view.bounds.origin; + + UIView* view = [[UIView alloc] initWithFrame:webViewBounds]; + view.translatesAutoresizingMaskIntoConstraints = NO; + [view setAlpha:0]; + + NSString* launchStoryboardName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UILaunchStoryboardName"]; + if (launchStoryboardName != nil) { + UIStoryboard* storyboard = [UIStoryboard storyboardWithName:launchStoryboardName bundle:[NSBundle mainBundle]]; + UIViewController* vc = [storyboard instantiateInitialViewController]; + [self addChildViewController:vc]; + + UIView* imgView = vc.view; + imgView.translatesAutoresizingMaskIntoConstraints = NO; + [view addSubview:imgView]; + + [NSLayoutConstraint activateConstraints:@[ + [NSLayoutConstraint constraintWithItem:imgView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeWidth multiplier:1 constant:0], + [NSLayoutConstraint constraintWithItem:imgView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeHeight multiplier:1 constant:0], + [NSLayoutConstraint constraintWithItem:imgView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterY multiplier:1 constant:0], + [NSLayoutConstraint constraintWithItem:imgView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0] + ]]; + } + + self.launchView = view; + [self.view addSubview:view]; + + [NSLayoutConstraint activateConstraints:@[ + [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:1 constant:0], + [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:1 constant:0], + [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1 constant:0], + [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0] + ]]; +} + +- (void)createGapView +{ + CGRect webViewBounds = self.view.bounds; + webViewBounds.origin = self.view.bounds.origin; + + UIView* view = [self newCordovaViewWithFrame:webViewBounds]; + view.hidden = YES; + view.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + + [self.view addSubview:view]; + [self.view sendSubviewToBack:view]; +} + +- (void)didReceiveMemoryWarning +{ + // iterate through all the plugin objects, and call hasPendingOperation + // if at least one has a pending operation, we don't call [super didReceiveMemoryWarning] + + NSEnumerator* enumerator = [self.pluginObjects objectEnumerator]; + CDVPlugin* plugin; + + BOOL doPurge = YES; + + while ((plugin = [enumerator nextObject])) { + if (plugin.hasPendingOperation) { + NSLog(@"Plugin '%@' has a pending operation, memory purge is delayed for didReceiveMemoryWarning.", NSStringFromClass([plugin class])); + doPurge = NO; + } + } + + if (doPurge) { + // Releases the view if it doesn't have a superview. + [super didReceiveMemoryWarning]; + } + + // Release any cached data, images, etc. that aren't in use. +} + +#pragma mark CordovaCommands + +- (void)registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className +{ + if ([plugin respondsToSelector:@selector(setViewController:)]) { + [plugin setViewController:self]; + } + + if ([plugin respondsToSelector:@selector(setCommandDelegate:)]) { + [plugin setCommandDelegate:_commandDelegate]; + } + + [self.pluginObjects setObject:plugin forKey:className]; + [plugin pluginInitialize]; +} + +- (void)registerPlugin:(CDVPlugin*)plugin withPluginName:(NSString*)pluginName +{ + if ([plugin respondsToSelector:@selector(setViewController:)]) { + [plugin setViewController:self]; + } + + if ([plugin respondsToSelector:@selector(setCommandDelegate:)]) { + [plugin setCommandDelegate:_commandDelegate]; + } + + NSString* className = NSStringFromClass([plugin class]); + [self.pluginObjects setObject:plugin forKey:className]; + [self.pluginsMap setValue:className forKey:[pluginName lowercaseString]]; + [plugin pluginInitialize]; +} + +/** + Returns an instance of a CordovaCommand object, based on its name. If one exists already, it is returned. + */ +- (nullable id)getCommandInstance:(NSString*)pluginName +{ + // first, we try to find the pluginName in the pluginsMap + // (acts as a allowList as well) if it does not exist, we return nil + // NOTE: plugin names are matched as lowercase to avoid problems - however, a + // possible issue is there can be duplicates possible if you had: + // "org.apache.cordova.Foo" and "org.apache.cordova.foo" - only the lower-cased entry will match + NSString* className = [self.pluginsMap objectForKey:[pluginName lowercaseString]]; + + if (className == nil) { + return nil; + } + + id obj = [self.pluginObjects objectForKey:className]; + if (!obj) { + obj = [[NSClassFromString(className)alloc] initWithWebViewEngine:_webViewEngine]; + if (!obj) { + NSString* fullClassName = [NSString stringWithFormat:@"%@.%@", + NSBundle.mainBundle.infoDictionary[@"CFBundleExecutable"], + className]; + obj = [[NSClassFromString(fullClassName)alloc] initWithWebViewEngine:_webViewEngine]; + } + + if (obj != nil) { + [self registerPlugin:obj withClassName:className]; + } else { + NSLog(@"CDVPlugin class %@ (pluginName: %@) does not exist.", className, pluginName); + } + } + return obj; +} + +#pragma mark - + +- (nullable NSString*)appURLScheme +{ + NSString* URLScheme = nil; + + NSArray* URLTypes = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleURLTypes"]; + + if (URLTypes != nil) { + NSDictionary* dict = [URLTypes objectAtIndex:0]; + if (dict != nil) { + NSArray* URLSchemes = [dict objectForKey:@"CFBundleURLSchemes"]; + if (URLSchemes != nil) { + URLScheme = [URLSchemes objectAtIndex:0]; + } + } + } + + return URLScheme; +} + +#pragma mark - +#pragma mark UIApplicationDelegate impl + +/* + This method lets your application know that it is about to be terminated and purged from memory entirely + */ +- (void)onAppWillTerminate:(NSNotification*)notification +{ + // empty the tmp directory + NSFileManager* fileMgr = [[NSFileManager alloc] init]; + NSError* __autoreleasing err = nil; + + // clear contents of NSTemporaryDirectory + NSString* tempDirectoryPath = NSTemporaryDirectory(); + NSDirectoryEnumerator* directoryEnumerator = [fileMgr enumeratorAtPath:tempDirectoryPath]; + NSString* fileName = nil; + BOOL result; + + while ((fileName = [directoryEnumerator nextObject])) { + NSString* filePath = [tempDirectoryPath stringByAppendingPathComponent:fileName]; + result = [fileMgr removeItemAtPath:filePath error:&err]; + if (!result && err) { + NSLog(@"Failed to delete: %@ (error: %@)", filePath, err); + } + } +} + +- (bool)isUrlEmpty:(NSURL *)url +{ + if (!url || (url == (id) [NSNull null])) { + return true; + } + NSString *urlAsString = [url absoluteString]; + return (urlAsString == (id) [NSNull null] || [urlAsString length]==0 || [urlAsString isEqualToString:@"about:blank"]); +} + +- (bool)checkAndReinitViewUrl +{ + NSURL* appURL = [self appUrl]; + if ([self isUrlEmpty: [self.webViewEngine URL]] && ![self isUrlEmpty: appURL]) { + NSURLRequest* appReq = [NSURLRequest requestWithURL:appURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0]; + [self.webViewEngine loadRequest:appReq]; + return true; + } + return false; +} + +/* + This method is called to let your application know that it is about to move from the active to inactive state. + You should use this method to pause ongoing tasks, disable timer, ... + */ +- (void)onAppWillResignActive:(NSNotification*)notification +{ + [self checkAndReinitViewUrl]; + // NSLog(@"%@",@"applicationWillResignActive"); + [self.commandDelegate evalJs:@"cordova.fireDocumentEvent('resign');" scheduledOnRunLoop:NO]; +} + +/* + In iOS 4.0 and later, this method is called as part of the transition from the background to the inactive state. + You can use this method to undo many of the changes you made to your application upon entering the background. + invariably followed by applicationDidBecomeActive + */ +- (void)onAppWillEnterForeground:(NSNotification*)notification +{ + [self checkAndReinitViewUrl]; + // NSLog(@"%@",@"applicationWillEnterForeground"); + [self.commandDelegate evalJs:@"cordova.fireDocumentEvent('resume');"]; + + if (!IsAtLeastiOSVersion(@"11.0")) { + /** Clipboard fix **/ + UIPasteboard* pasteboard = [UIPasteboard generalPasteboard]; + NSString* string = pasteboard.string; + if (string) { + [pasteboard setValue:string forPasteboardType:@"public.text"]; + } + } +} + +// This method is called to let your application know that it moved from the inactive to active state. +- (void)onAppDidBecomeActive:(NSNotification*)notification +{ + [self checkAndReinitViewUrl]; + // NSLog(@"%@",@"applicationDidBecomeActive"); + [self.commandDelegate evalJs:@"cordova.fireDocumentEvent('active');"]; +} + +/* + In iOS 4.0 and later, this method is called instead of the applicationWillTerminate: method + when the user quits an application that supports background execution. + */ +- (void)onAppDidEnterBackground:(NSNotification*)notification +{ + [self checkAndReinitViewUrl]; + // NSLog(@"%@",@"applicationDidEnterBackground"); + [self.commandDelegate evalJs:@"cordova.fireDocumentEvent('pause', null, true);" scheduledOnRunLoop:NO]; +} + +/** + Show the webview and fade out the intermediary view + This is to prevent the flashing of the mainViewController + */ +- (void)onWebViewPageDidLoad:(NSNotification*)notification +{ + self.webView.hidden = NO; + + if ([self.settings cordovaBoolSettingForKey:@"AutoHideSplashScreen" defaultValue:YES]) { + CGFloat splashScreenDelaySetting = [self.settings cordovaFloatSettingForKey:@"SplashScreenDelay" defaultValue:0]; + + if (splashScreenDelaySetting == 0) { + [self showLaunchScreen:NO]; + } else { + // Divide by 1000 because config returns milliseconds and NSTimer takes seconds + CGFloat splashScreenDelay = splashScreenDelaySetting / 1000; + + [NSTimer scheduledTimerWithTimeInterval:splashScreenDelay repeats:NO block:^(NSTimer * _Nonnull timer) { + [self showLaunchScreen:NO]; + }]; + } + } +} + +/** + Method to be called from the plugin JavaScript to show or hide the launch screen. + */ +- (void)showLaunchScreen:(BOOL)visible +{ + CGFloat fadeSplashScreenDuration = [self.settings cordovaFloatSettingForKey:@"FadeSplashScreenDuration" defaultValue:250]; + + // Setting minimum value for fade to 0.25 seconds + fadeSplashScreenDuration = fadeSplashScreenDuration < 250 ? 250 : fadeSplashScreenDuration; + + // AnimateWithDuration takes seconds but cordova documentation specifies milliseconds + CGFloat fadeDuration = fadeSplashScreenDuration/1000; + + [UIView animateWithDuration:fadeDuration animations:^{ + [self.launchView setAlpha:(visible ? 1 : 0)]; + + if (!visible) { + [self.webView becomeFirstResponder]; + } + }]; +} + +// /////////////////////// + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [_commandQueue dispose]; + [[self.pluginObjects allValues] makeObjectsPerformSelector:@selector(dispose)]; + + [self.webViewEngine loadHTMLString:@"about:blank" baseURL:nil]; + [self.pluginObjects removeAllObjects]; + [self.webView removeFromSuperview]; + self.webViewEngine = nil; +} + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVWebViewProcessPoolFactory.m b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVWebViewProcessPoolFactory.m new file mode 100644 index 00000000..3781f48a --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/CDVWebViewProcessPoolFactory.m @@ -0,0 +1,49 @@ +/* + 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. + */ + +@import Foundation; +@import WebKit; +#import + +static CDVWebViewProcessPoolFactory *factory = nil; + +@implementation CDVWebViewProcessPoolFactory + ++ (instancetype)sharedFactory +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + factory = [[CDVWebViewProcessPoolFactory alloc] init]; + }); + + return factory; +} + +- (instancetype)init +{ + if (self = [super init]) { + _sharedPool = [[WKProcessPool alloc] init]; + } + return self; +} + +- (WKProcessPool*) sharedProcessPool { + return _sharedPool; +} +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/NSDictionary+CordovaPreferences.m b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/NSDictionary+CordovaPreferences.m new file mode 100644 index 00000000..a60004d9 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/NSDictionary+CordovaPreferences.m @@ -0,0 +1,93 @@ +/* + 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. + */ + +@import Foundation; + +#import + +@implementation NSDictionary (CordovaPreferences) + +- (id)cordovaSettingForKey:(NSString*)key +{ + return [self objectForKey:[key lowercaseString]]; +} + +- (BOOL)cordovaBoolSettingForKey:(NSString*)key defaultValue:(BOOL)defaultValue +{ + BOOL value = defaultValue; + id prefObj = [self cordovaSettingForKey:key]; + + if (prefObj == nil) { + NSLog(@"The preference key \"%@\" is not defined and will default to \"%@\"", + key, + (defaultValue ? @"TRUE" : @"FALSE")); + + return value; + } + + if ([prefObj isKindOfClass:NSString.class]) { + prefObj = [prefObj lowercaseString]; + + if ( + // True Case + [prefObj isEqualToString:@"true"] || + [prefObj isEqualToString:@"1"] || + // False Case + [prefObj isEqualToString:@"false"] || + [prefObj isEqualToString:@"0"] + ) + { + value = [prefObj isEqualToString:@"true"] || [prefObj isEqualToString:@"1"]; + } + } else if ( + [prefObj isKindOfClass:NSNumber.class] && + ( + [prefObj isEqual: @YES] || + [prefObj isEqual: @NO] + ) + ) + { + value = [prefObj isEqual: @YES]; + } + + return value; +} + +- (CGFloat)cordovaFloatSettingForKey:(NSString*)key defaultValue:(CGFloat)defaultValue +{ + CGFloat value = defaultValue; + id prefObj = [self cordovaSettingForKey:key]; + + if (prefObj != nil) { + value = [prefObj floatValue]; + } + + return value; +} + +@end + +@implementation NSMutableDictionary (CordovaPreferences) + +- (void)setCordovaSetting:(id)value forKey:(NSString*)key +{ + [self setObject:value forKey:[key lowercaseString]]; +} + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/NSMutableArray+QueueAdditions.m b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/NSMutableArray+QueueAdditions.m new file mode 100644 index 00000000..65b97a0e --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/Classes/Public/NSMutableArray+QueueAdditions.m @@ -0,0 +1,58 @@ +/* + 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. + */ + +#import + +@implementation NSMutableArray (QueueAdditions) + +- (id)cdv_queueHead +{ + if ([self count] == 0) { + return nil; + } + + return [self objectAtIndex:0]; +} + +- (__autoreleasing id)cdv_dequeue +{ + if ([self count] == 0) { + return nil; + } + + id head = [self objectAtIndex:0]; + if (head != nil) { + // [[head retain] autorelease]; ARC - the __autoreleasing on the return value should so the same thing + [self removeObjectAtIndex:0]; + } + + return head; +} + +- (id)cdv_pop +{ + return [self cdv_dequeue]; +} + +- (void)cdv_enqueue:(id)object +{ + [self addObject:object]; +} + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/CordovaLib.xcodeproj/project.pbxproj b/MovieVerse-Mobile/platforms/ios/CordovaLib/CordovaLib.xcodeproj/project.pbxproj new file mode 100644 index 00000000..04a7b35c --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/CordovaLib.xcodeproj/project.pbxproj @@ -0,0 +1,815 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 52; + objects = { + +/* Begin PBXBuildFile section */ + 28BFF9141F355A4E00DDF01A /* CDVLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 28BFF9121F355A4E00DDF01A /* CDVLogger.h */; }; + 28BFF9151F355A4E00DDF01A /* CDVLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 28BFF9131F355A4E00DDF01A /* CDVLogger.m */; }; + 2F4D42BC23F218BA00501999 /* CDVURLSchemeHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F4D42BA23F218BA00501999 /* CDVURLSchemeHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2F4D42BD23F218BA00501999 /* CDVURLSchemeHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F4D42BB23F218BA00501999 /* CDVURLSchemeHandler.m */; }; + 2FCCEA17247E7366007276A8 /* CDVLaunchScreen.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E714D3423F535B500A321AF /* CDVLaunchScreen.m */; }; + 2FCCEA18247E7366007276A8 /* CDVLaunchScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E714D3223F535B500A321AF /* CDVLaunchScreen.h */; }; + 3093E2231B16D6A3003F381A /* CDVIntentAndNavigationFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 3093E2211B16D6A3003F381A /* CDVIntentAndNavigationFilter.h */; }; + 3093E2241B16D6A3003F381A /* CDVIntentAndNavigationFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 3093E2221B16D6A3003F381A /* CDVIntentAndNavigationFilter.m */; }; + 4E23F8FB23E16E96006CD852 /* CDVWebViewProcessPoolFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E23F8F523E16E96006CD852 /* CDVWebViewProcessPoolFactory.m */; }; + 4E23F8FC23E16E96006CD852 /* CDVWebViewUIDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E23F8F623E16E96006CD852 /* CDVWebViewUIDelegate.h */; }; + 4E23F8FD23E16E96006CD852 /* CDVWebViewUIDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E23F8F723E16E96006CD852 /* CDVWebViewUIDelegate.m */; }; + 4E23F8FE23E16E96006CD852 /* CDVWebViewEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E23F8F823E16E96006CD852 /* CDVWebViewEngine.m */; }; + 4E23F8FF23E16E96006CD852 /* CDVWebViewProcessPoolFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E23F8F923E16E96006CD852 /* CDVWebViewProcessPoolFactory.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4E23F90023E16E96006CD852 /* CDVWebViewEngine.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E23F8FA23E16E96006CD852 /* CDVWebViewEngine.h */; }; + 4E23F90323E17FFA006CD852 /* CDVWebViewUIDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E23F8F623E16E96006CD852 /* CDVWebViewUIDelegate.h */; }; + 4E714D3623F535B500A321AF /* CDVLaunchScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E714D3223F535B500A321AF /* CDVLaunchScreen.h */; }; + 4E714D3823F535B500A321AF /* CDVLaunchScreen.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E714D3423F535B500A321AF /* CDVLaunchScreen.m */; }; + 4F56D82D254A2EB50063F1D6 /* CDVWebViewEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E23F8F823E16E96006CD852 /* CDVWebViewEngine.m */; }; + 4F56D830254A2ED70063F1D6 /* CDVWebViewUIDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E23F8F723E16E96006CD852 /* CDVWebViewUIDelegate.m */; }; + 4F56D833254A2ED90063F1D6 /* CDVWebViewProcessPoolFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E23F8F523E16E96006CD852 /* CDVWebViewProcessPoolFactory.m */; }; + 4F56D836254A2EE10063F1D6 /* CDVWebViewEngine.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E23F8FA23E16E96006CD852 /* CDVWebViewEngine.h */; }; + 4F56D839254A2EE40063F1D6 /* CDVWebViewProcessPoolFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E23F8F923E16E96006CD852 /* CDVWebViewProcessPoolFactory.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4F56D83C254A2F2F0063F1D6 /* CDVURLSchemeHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F4D42BB23F218BA00501999 /* CDVURLSchemeHandler.m */; }; + 7E7F69B91ABA3692007546F4 /* CDVHandleOpenURL.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CF81AB9028C008C4574 /* CDVHandleOpenURL.h */; }; + 7ED95D021AB9028C008C4574 /* CDVDebug.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CF21AB9028C008C4574 /* CDVDebug.h */; }; + 7ED95D031AB9028C008C4574 /* CDVJSON_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CF31AB9028C008C4574 /* CDVJSON_private.h */; }; + 7ED95D041AB9028C008C4574 /* CDVJSON_private.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95CF41AB9028C008C4574 /* CDVJSON_private.m */; }; + 7ED95D051AB9028C008C4574 /* CDVPlugin+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CF51AB9028C008C4574 /* CDVPlugin+Private.h */; }; + 7ED95D071AB9028C008C4574 /* CDVHandleOpenURL.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95CF91AB9028C008C4574 /* CDVHandleOpenURL.m */; }; + 7ED95D351AB9029B008C4574 /* CDV.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D0F1AB9029B008C4574 /* CDV.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ED95D361AB9029B008C4574 /* CDVAppDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D101AB9029B008C4574 /* CDVAppDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ED95D371AB9029B008C4574 /* CDVAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D111AB9029B008C4574 /* CDVAppDelegate.m */; }; + 7ED95D381AB9029B008C4574 /* CDVAvailability.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D121AB9029B008C4574 /* CDVAvailability.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ED95D391AB9029B008C4574 /* CDVAvailabilityDeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D131AB9029B008C4574 /* CDVAvailabilityDeprecated.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ED95D3A1AB9029B008C4574 /* CDVCommandDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D141AB9029B008C4574 /* CDVCommandDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ED95D3B1AB9029B008C4574 /* CDVCommandDelegateImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D151AB9029B008C4574 /* CDVCommandDelegateImpl.h */; }; + 7ED95D3C1AB9029B008C4574 /* CDVCommandDelegateImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D161AB9029B008C4574 /* CDVCommandDelegateImpl.m */; }; + 7ED95D3D1AB9029B008C4574 /* CDVCommandQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D171AB9029B008C4574 /* CDVCommandQueue.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ED95D3E1AB9029B008C4574 /* CDVCommandQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D181AB9029B008C4574 /* CDVCommandQueue.m */; }; + 7ED95D3F1AB9029B008C4574 /* CDVConfigParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D191AB9029B008C4574 /* CDVConfigParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ED95D401AB9029B008C4574 /* CDVConfigParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D1A1AB9029B008C4574 /* CDVConfigParser.m */; }; + 7ED95D411AB9029B008C4574 /* CDVInvokedUrlCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D1B1AB9029B008C4574 /* CDVInvokedUrlCommand.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ED95D421AB9029B008C4574 /* CDVInvokedUrlCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D1C1AB9029B008C4574 /* CDVInvokedUrlCommand.m */; }; + 7ED95D431AB9029B008C4574 /* CDVPlugin+Resources.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D1D1AB9029B008C4574 /* CDVPlugin+Resources.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ED95D441AB9029B008C4574 /* CDVPlugin+Resources.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D1E1AB9029B008C4574 /* CDVPlugin+Resources.m */; }; + 7ED95D451AB9029B008C4574 /* CDVPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D1F1AB9029B008C4574 /* CDVPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ED95D461AB9029B008C4574 /* CDVPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D201AB9029B008C4574 /* CDVPlugin.m */; }; + 7ED95D471AB9029B008C4574 /* CDVPluginResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D211AB9029B008C4574 /* CDVPluginResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ED95D481AB9029B008C4574 /* CDVPluginResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D221AB9029B008C4574 /* CDVPluginResult.m */; }; + 7ED95D491AB9029B008C4574 /* CDVScreenOrientationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D231AB9029B008C4574 /* CDVScreenOrientationDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ED95D4A1AB9029B008C4574 /* CDVTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D241AB9029B008C4574 /* CDVTimer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ED95D4B1AB9029B008C4574 /* CDVTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D251AB9029B008C4574 /* CDVTimer.m */; }; + 7ED95D501AB9029B008C4574 /* CDVViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D2A1AB9029B008C4574 /* CDVViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ED95D511AB9029B008C4574 /* CDVViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D2B1AB9029B008C4574 /* CDVViewController.m */; }; + 7ED95D521AB9029B008C4574 /* CDVWebViewEngineProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D2C1AB9029B008C4574 /* CDVWebViewEngineProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ED95D531AB9029B008C4574 /* CDVAllowList.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D2D1AB9029B008C4574 /* CDVAllowList.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ED95D541AB9029B008C4574 /* CDVAllowList.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D2E1AB9029B008C4574 /* CDVAllowList.m */; }; + 7ED95D571AB9029B008C4574 /* NSDictionary+CordovaPreferences.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D311AB9029B008C4574 /* NSDictionary+CordovaPreferences.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ED95D581AB9029B008C4574 /* NSDictionary+CordovaPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D321AB9029B008C4574 /* NSDictionary+CordovaPreferences.m */; }; + 7ED95D591AB9029B008C4574 /* NSMutableArray+QueueAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D331AB9029B008C4574 /* NSMutableArray+QueueAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ED95D5A1AB9029B008C4574 /* NSMutableArray+QueueAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D341AB9029B008C4574 /* NSMutableArray+QueueAdditions.m */; }; + 9052DE712150D040008E83D4 /* CDVAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D111AB9029B008C4574 /* CDVAppDelegate.m */; }; + 9052DE722150D040008E83D4 /* CDVCommandDelegateImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D161AB9029B008C4574 /* CDVCommandDelegateImpl.m */; }; + 9052DE732150D040008E83D4 /* CDVCommandQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D181AB9029B008C4574 /* CDVCommandQueue.m */; }; + 9052DE742150D040008E83D4 /* CDVConfigParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D1A1AB9029B008C4574 /* CDVConfigParser.m */; }; + 9052DE752150D040008E83D4 /* CDVInvokedUrlCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D1C1AB9029B008C4574 /* CDVInvokedUrlCommand.m */; }; + 9052DE762150D040008E83D4 /* CDVPlugin+Resources.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D1E1AB9029B008C4574 /* CDVPlugin+Resources.m */; }; + 9052DE772150D040008E83D4 /* CDVPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D201AB9029B008C4574 /* CDVPlugin.m */; }; + 9052DE782150D040008E83D4 /* CDVPluginResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D221AB9029B008C4574 /* CDVPluginResult.m */; }; + 9052DE792150D040008E83D4 /* CDVTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D251AB9029B008C4574 /* CDVTimer.m */; }; + 9052DE7C2150D040008E83D4 /* CDVViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D2B1AB9029B008C4574 /* CDVViewController.m */; }; + 9052DE7D2150D040008E83D4 /* CDVAllowList.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D2E1AB9029B008C4574 /* CDVAllowList.m */; }; + 9052DE7E2150D040008E83D4 /* NSDictionary+CordovaPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D321AB9029B008C4574 /* NSDictionary+CordovaPreferences.m */; }; + 9052DE7F2150D040008E83D4 /* NSMutableArray+QueueAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D341AB9029B008C4574 /* NSMutableArray+QueueAdditions.m */; }; + 9052DE802150D040008E83D4 /* CDVJSON_private.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95CF41AB9028C008C4574 /* CDVJSON_private.m */; }; + 9052DE812150D040008E83D4 /* CDVLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 28BFF9131F355A4E00DDF01A /* CDVLogger.m */; }; + 9052DE822150D040008E83D4 /* CDVGestureHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = A3B082D31BB15CEA00D8DC35 /* CDVGestureHandler.m */; }; + 9052DE832150D040008E83D4 /* CDVIntentAndNavigationFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 3093E2221B16D6A3003F381A /* CDVIntentAndNavigationFilter.m */; }; + 9052DE842150D040008E83D4 /* CDVHandleOpenURL.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95CF91AB9028C008C4574 /* CDVHandleOpenURL.m */; }; + 9052DE892150D06B008E83D4 /* CDVDebug.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CF21AB9028C008C4574 /* CDVDebug.h */; }; + 9052DE8A2150D06B008E83D4 /* CDVJSON_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CF31AB9028C008C4574 /* CDVJSON_private.h */; }; + 9052DE8B2150D06B008E83D4 /* CDVPlugin+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CF51AB9028C008C4574 /* CDVPlugin+Private.h */; }; + 9052DE8C2150D06B008E83D4 /* CDVLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 28BFF9121F355A4E00DDF01A /* CDVLogger.h */; }; + 9052DE8D2150D06B008E83D4 /* CDVGestureHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = A3B082D21BB15CEA00D8DC35 /* CDVGestureHandler.h */; }; + 9052DE8E2150D06B008E83D4 /* CDVIntentAndNavigationFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 3093E2211B16D6A3003F381A /* CDVIntentAndNavigationFilter.h */; }; + 9052DE8F2150D06B008E83D4 /* CDVHandleOpenURL.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CF81AB9028C008C4574 /* CDVHandleOpenURL.h */; }; + 9059F51C26F2CE2400B3B2B7 /* CDVURLSchemeHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F4D42BA23F218BA00501999 /* CDVURLSchemeHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A3B082D41BB15CEA00D8DC35 /* CDVGestureHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = A3B082D21BB15CEA00D8DC35 /* CDVGestureHandler.h */; }; + A3B082D51BB15CEA00D8DC35 /* CDVGestureHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = A3B082D31BB15CEA00D8DC35 /* CDVGestureHandler.m */; }; + C0C01EB61E3911D50056E6CB /* Cordova.h in Headers */ = {isa = PBXBuildFile; fileRef = C0C01EB41E3911D50056E6CB /* Cordova.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C0C01EBB1E39131A0056E6CB /* CDV.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D0F1AB9029B008C4574 /* CDV.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C0C01EBC1E39131A0056E6CB /* CDVAppDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D101AB9029B008C4574 /* CDVAppDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C0C01EBD1E39131A0056E6CB /* CDVAvailability.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D121AB9029B008C4574 /* CDVAvailability.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C0C01EBE1E39131A0056E6CB /* CDVAvailabilityDeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D131AB9029B008C4574 /* CDVAvailabilityDeprecated.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C0C01EBF1E39131A0056E6CB /* CDVCommandDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D141AB9029B008C4574 /* CDVCommandDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C0C01EC01E39131A0056E6CB /* CDVCommandDelegateImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D151AB9029B008C4574 /* CDVCommandDelegateImpl.h */; }; + C0C01EC11E39131A0056E6CB /* CDVCommandQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D171AB9029B008C4574 /* CDVCommandQueue.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C0C01EC21E39131A0056E6CB /* CDVConfigParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D191AB9029B008C4574 /* CDVConfigParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C0C01EC31E39131A0056E6CB /* CDVInvokedUrlCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D1B1AB9029B008C4574 /* CDVInvokedUrlCommand.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C0C01EC41E39131A0056E6CB /* CDVPlugin+Resources.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D1D1AB9029B008C4574 /* CDVPlugin+Resources.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C0C01EC51E39131A0056E6CB /* CDVPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D1F1AB9029B008C4574 /* CDVPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C0C01EC61E39131A0056E6CB /* CDVPluginResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D211AB9029B008C4574 /* CDVPluginResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C0C01EC71E39131A0056E6CB /* CDVScreenOrientationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D231AB9029B008C4574 /* CDVScreenOrientationDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C0C01EC81E39131A0056E6CB /* CDVTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D241AB9029B008C4574 /* CDVTimer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C0C01ECB1E39131A0056E6CB /* CDVViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D2A1AB9029B008C4574 /* CDVViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C0C01ECC1E39131A0056E6CB /* CDVWebViewEngineProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D2C1AB9029B008C4574 /* CDVWebViewEngineProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C0C01ECD1E39131A0056E6CB /* CDVAllowList.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D2D1AB9029B008C4574 /* CDVAllowList.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C0C01ECE1E39131A0056E6CB /* NSDictionary+CordovaPreferences.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D311AB9029B008C4574 /* NSDictionary+CordovaPreferences.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C0C01ECF1E39131A0056E6CB /* NSMutableArray+QueueAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D331AB9029B008C4574 /* NSMutableArray+QueueAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 28BFF9121F355A4E00DDF01A /* CDVLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVLogger.h; sourceTree = ""; }; + 28BFF9131F355A4E00DDF01A /* CDVLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVLogger.m; sourceTree = ""; }; + 2F4D42BA23F218BA00501999 /* CDVURLSchemeHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CDVURLSchemeHandler.h; sourceTree = ""; }; + 2F4D42BB23F218BA00501999 /* CDVURLSchemeHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CDVURLSchemeHandler.m; sourceTree = ""; }; + 3093E2211B16D6A3003F381A /* CDVIntentAndNavigationFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVIntentAndNavigationFilter.h; sourceTree = ""; }; + 3093E2221B16D6A3003F381A /* CDVIntentAndNavigationFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVIntentAndNavigationFilter.m; sourceTree = ""; }; + 4E23F8F523E16E96006CD852 /* CDVWebViewProcessPoolFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVWebViewProcessPoolFactory.m; sourceTree = ""; }; + 4E23F8F623E16E96006CD852 /* CDVWebViewUIDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVWebViewUIDelegate.h; sourceTree = ""; }; + 4E23F8F723E16E96006CD852 /* CDVWebViewUIDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVWebViewUIDelegate.m; sourceTree = ""; }; + 4E23F8F823E16E96006CD852 /* CDVWebViewEngine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVWebViewEngine.m; sourceTree = ""; }; + 4E23F8F923E16E96006CD852 /* CDVWebViewProcessPoolFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVWebViewProcessPoolFactory.h; sourceTree = ""; }; + 4E23F8FA23E16E96006CD852 /* CDVWebViewEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVWebViewEngine.h; sourceTree = ""; }; + 4E714D3223F535B500A321AF /* CDVLaunchScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVLaunchScreen.h; sourceTree = ""; }; + 4E714D3423F535B500A321AF /* CDVLaunchScreen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVLaunchScreen.m; sourceTree = ""; }; + 68A32D7114102E1C006B237C /* libCordova.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCordova.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 7ED95CF21AB9028C008C4574 /* CDVDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVDebug.h; sourceTree = ""; }; + 7ED95CF31AB9028C008C4574 /* CDVJSON_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVJSON_private.h; sourceTree = ""; }; + 7ED95CF41AB9028C008C4574 /* CDVJSON_private.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVJSON_private.m; sourceTree = ""; }; + 7ED95CF51AB9028C008C4574 /* CDVPlugin+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CDVPlugin+Private.h"; sourceTree = ""; }; + 7ED95CF81AB9028C008C4574 /* CDVHandleOpenURL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVHandleOpenURL.h; sourceTree = ""; }; + 7ED95CF91AB9028C008C4574 /* CDVHandleOpenURL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVHandleOpenURL.m; sourceTree = ""; }; + 7ED95D0F1AB9029B008C4574 /* CDV.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDV.h; sourceTree = ""; }; + 7ED95D101AB9029B008C4574 /* CDVAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVAppDelegate.h; sourceTree = ""; }; + 7ED95D111AB9029B008C4574 /* CDVAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVAppDelegate.m; sourceTree = ""; }; + 7ED95D121AB9029B008C4574 /* CDVAvailability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVAvailability.h; sourceTree = ""; }; + 7ED95D131AB9029B008C4574 /* CDVAvailabilityDeprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVAvailabilityDeprecated.h; sourceTree = ""; }; + 7ED95D141AB9029B008C4574 /* CDVCommandDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVCommandDelegate.h; sourceTree = ""; }; + 7ED95D151AB9029B008C4574 /* CDVCommandDelegateImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVCommandDelegateImpl.h; sourceTree = ""; }; + 7ED95D161AB9029B008C4574 /* CDVCommandDelegateImpl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVCommandDelegateImpl.m; sourceTree = ""; }; + 7ED95D171AB9029B008C4574 /* CDVCommandQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVCommandQueue.h; sourceTree = ""; }; + 7ED95D181AB9029B008C4574 /* CDVCommandQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVCommandQueue.m; sourceTree = ""; }; + 7ED95D191AB9029B008C4574 /* CDVConfigParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVConfigParser.h; sourceTree = ""; }; + 7ED95D1A1AB9029B008C4574 /* CDVConfigParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVConfigParser.m; sourceTree = ""; }; + 7ED95D1B1AB9029B008C4574 /* CDVInvokedUrlCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVInvokedUrlCommand.h; sourceTree = ""; }; + 7ED95D1C1AB9029B008C4574 /* CDVInvokedUrlCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVInvokedUrlCommand.m; sourceTree = ""; }; + 7ED95D1D1AB9029B008C4574 /* CDVPlugin+Resources.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CDVPlugin+Resources.h"; sourceTree = ""; }; + 7ED95D1E1AB9029B008C4574 /* CDVPlugin+Resources.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CDVPlugin+Resources.m"; sourceTree = ""; }; + 7ED95D1F1AB9029B008C4574 /* CDVPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVPlugin.h; sourceTree = ""; }; + 7ED95D201AB9029B008C4574 /* CDVPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVPlugin.m; sourceTree = ""; }; + 7ED95D211AB9029B008C4574 /* CDVPluginResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVPluginResult.h; sourceTree = ""; }; + 7ED95D221AB9029B008C4574 /* CDVPluginResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVPluginResult.m; sourceTree = ""; }; + 7ED95D231AB9029B008C4574 /* CDVScreenOrientationDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVScreenOrientationDelegate.h; sourceTree = ""; }; + 7ED95D241AB9029B008C4574 /* CDVTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVTimer.h; sourceTree = ""; }; + 7ED95D251AB9029B008C4574 /* CDVTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVTimer.m; sourceTree = ""; }; + 7ED95D2A1AB9029B008C4574 /* CDVViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVViewController.h; sourceTree = ""; }; + 7ED95D2B1AB9029B008C4574 /* CDVViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVViewController.m; sourceTree = ""; }; + 7ED95D2C1AB9029B008C4574 /* CDVWebViewEngineProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVWebViewEngineProtocol.h; sourceTree = ""; }; + 7ED95D2D1AB9029B008C4574 /* CDVAllowList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVAllowList.h; sourceTree = ""; }; + 7ED95D2E1AB9029B008C4574 /* CDVAllowList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVAllowList.m; sourceTree = ""; }; + 7ED95D311AB9029B008C4574 /* NSDictionary+CordovaPreferences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+CordovaPreferences.h"; sourceTree = ""; }; + 7ED95D321AB9029B008C4574 /* NSDictionary+CordovaPreferences.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+CordovaPreferences.m"; sourceTree = ""; }; + 7ED95D331AB9029B008C4574 /* NSMutableArray+QueueAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+QueueAdditions.h"; sourceTree = ""; }; + 7ED95D341AB9029B008C4574 /* NSMutableArray+QueueAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray+QueueAdditions.m"; sourceTree = ""; }; + A3B082D21BB15CEA00D8DC35 /* CDVGestureHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVGestureHandler.h; sourceTree = ""; }; + A3B082D31BB15CEA00D8DC35 /* CDVGestureHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVGestureHandler.m; sourceTree = ""; }; + C0C01EB21E3911D50056E6CB /* Cordova.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Cordova.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C0C01EB41E3911D50056E6CB /* Cordova.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Cordova.h; sourceTree = ""; }; + C0C01EB51E3911D50056E6CB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + C0C01EAE1E3911D50056E6CB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D2AAC07C0554694100DB518D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 034768DFFF38A50411DB9C8B /* Products */ = { + isa = PBXGroup; + children = ( + 68A32D7114102E1C006B237C /* libCordova.a */, + C0C01EB21E3911D50056E6CB /* Cordova.framework */, + ); + name = Products; + sourceTree = ""; + }; + 0867D691FE84028FC02AAC07 = { + isa = PBXGroup; + children = ( + 9064EF5E26FAB74200C9D65B /* include */, + 7ED95D0E1AB9029B008C4574 /* Public */, + 7ED95CF11AB9028C008C4574 /* Private */, + C0C01EB31E3911D50056E6CB /* Cordova */, + 034768DFFF38A50411DB9C8B /* Products */, + ); + sourceTree = ""; + }; + 28BFF9111F355A1D00DDF01A /* CDVLogger */ = { + isa = PBXGroup; + children = ( + 28BFF9121F355A4E00DDF01A /* CDVLogger.h */, + 28BFF9131F355A4E00DDF01A /* CDVLogger.m */, + ); + path = CDVLogger; + sourceTree = ""; + }; + 3093E2201B16D6A3003F381A /* CDVIntentAndNavigationFilter */ = { + isa = PBXGroup; + children = ( + 3093E2211B16D6A3003F381A /* CDVIntentAndNavigationFilter.h */, + 3093E2221B16D6A3003F381A /* CDVIntentAndNavigationFilter.m */, + ); + path = CDVIntentAndNavigationFilter; + sourceTree = ""; + }; + 4E23F8F423E16D30006CD852 /* CDVWebViewEngine */ = { + isa = PBXGroup; + children = ( + 4E23F8FA23E16E96006CD852 /* CDVWebViewEngine.h */, + 4E23F8F823E16E96006CD852 /* CDVWebViewEngine.m */, + 4E23F8F623E16E96006CD852 /* CDVWebViewUIDelegate.h */, + 4E23F8F723E16E96006CD852 /* CDVWebViewUIDelegate.m */, + ); + path = CDVWebViewEngine; + sourceTree = ""; + }; + 4E714D3123F5356700A321AF /* CDVLaunchScreen */ = { + isa = PBXGroup; + children = ( + 4E714D3223F535B500A321AF /* CDVLaunchScreen.h */, + 4E714D3423F535B500A321AF /* CDVLaunchScreen.m */, + ); + path = CDVLaunchScreen; + sourceTree = ""; + }; + 7ED95CF11AB9028C008C4574 /* Private */ = { + isa = PBXGroup; + children = ( + 7ED95D151AB9029B008C4574 /* CDVCommandDelegateImpl.h */, + 7ED95D161AB9029B008C4574 /* CDVCommandDelegateImpl.m */, + 7ED95CF21AB9028C008C4574 /* CDVDebug.h */, + 7ED95CF31AB9028C008C4574 /* CDVJSON_private.h */, + 7ED95CF41AB9028C008C4574 /* CDVJSON_private.m */, + 7ED95CF51AB9028C008C4574 /* CDVPlugin+Private.h */, + 7ED95CF61AB9028C008C4574 /* Plugins */, + ); + name = Private; + path = Classes/Private; + sourceTree = ""; + }; + 7ED95CF61AB9028C008C4574 /* Plugins */ = { + isa = PBXGroup; + children = ( + 4E714D3123F5356700A321AF /* CDVLaunchScreen */, + 4E23F8F423E16D30006CD852 /* CDVWebViewEngine */, + 28BFF9111F355A1D00DDF01A /* CDVLogger */, + A3B082D11BB15CEA00D8DC35 /* CDVGestureHandler */, + 3093E2201B16D6A3003F381A /* CDVIntentAndNavigationFilter */, + 7ED95CF71AB9028C008C4574 /* CDVHandleOpenURL */, + ); + path = Plugins; + sourceTree = ""; + }; + 7ED95CF71AB9028C008C4574 /* CDVHandleOpenURL */ = { + isa = PBXGroup; + children = ( + 7ED95CF81AB9028C008C4574 /* CDVHandleOpenURL.h */, + 7ED95CF91AB9028C008C4574 /* CDVHandleOpenURL.m */, + ); + path = CDVHandleOpenURL; + sourceTree = ""; + }; + 7ED95D0E1AB9029B008C4574 /* Public */ = { + isa = PBXGroup; + children = ( + 7ED95D111AB9029B008C4574 /* CDVAppDelegate.m */, + 7ED95D181AB9029B008C4574 /* CDVCommandQueue.m */, + 7ED95D1A1AB9029B008C4574 /* CDVConfigParser.m */, + 7ED95D1C1AB9029B008C4574 /* CDVInvokedUrlCommand.m */, + 7ED95D1E1AB9029B008C4574 /* CDVPlugin+Resources.m */, + 7ED95D201AB9029B008C4574 /* CDVPlugin.m */, + 7ED95D221AB9029B008C4574 /* CDVPluginResult.m */, + 7ED95D251AB9029B008C4574 /* CDVTimer.m */, + 4E23F8F523E16E96006CD852 /* CDVWebViewProcessPoolFactory.m */, + 7ED95D2B1AB9029B008C4574 /* CDVViewController.m */, + 7ED95D2E1AB9029B008C4574 /* CDVAllowList.m */, + 7ED95D321AB9029B008C4574 /* NSDictionary+CordovaPreferences.m */, + 7ED95D341AB9029B008C4574 /* NSMutableArray+QueueAdditions.m */, + 2F4D42BB23F218BA00501999 /* CDVURLSchemeHandler.m */, + ); + name = Public; + path = Classes/Public; + sourceTree = ""; + }; + 9064EF5E26FAB74200C9D65B /* include */ = { + isa = PBXGroup; + children = ( + 9064EF5F26FAB74800C9D65B /* Cordova */, + ); + path = include; + sourceTree = ""; + }; + 9064EF5F26FAB74800C9D65B /* Cordova */ = { + isa = PBXGroup; + children = ( + 7ED95D0F1AB9029B008C4574 /* CDV.h */, + 7ED95D101AB9029B008C4574 /* CDVAppDelegate.h */, + 7ED95D121AB9029B008C4574 /* CDVAvailability.h */, + 7ED95D131AB9029B008C4574 /* CDVAvailabilityDeprecated.h */, + 7ED95D141AB9029B008C4574 /* CDVCommandDelegate.h */, + 7ED95D171AB9029B008C4574 /* CDVCommandQueue.h */, + 7ED95D191AB9029B008C4574 /* CDVConfigParser.h */, + 7ED95D1B1AB9029B008C4574 /* CDVInvokedUrlCommand.h */, + 7ED95D1D1AB9029B008C4574 /* CDVPlugin+Resources.h */, + 7ED95D1F1AB9029B008C4574 /* CDVPlugin.h */, + 7ED95D211AB9029B008C4574 /* CDVPluginResult.h */, + 7ED95D231AB9029B008C4574 /* CDVScreenOrientationDelegate.h */, + 7ED95D241AB9029B008C4574 /* CDVTimer.h */, + 7ED95D2A1AB9029B008C4574 /* CDVViewController.h */, + 4E23F8F923E16E96006CD852 /* CDVWebViewProcessPoolFactory.h */, + 7ED95D2C1AB9029B008C4574 /* CDVWebViewEngineProtocol.h */, + 7ED95D2D1AB9029B008C4574 /* CDVAllowList.h */, + 7ED95D311AB9029B008C4574 /* NSDictionary+CordovaPreferences.h */, + 7ED95D331AB9029B008C4574 /* NSMutableArray+QueueAdditions.h */, + 2F4D42BA23F218BA00501999 /* CDVURLSchemeHandler.h */, + ); + path = Cordova; + sourceTree = ""; + }; + A3B082D11BB15CEA00D8DC35 /* CDVGestureHandler */ = { + isa = PBXGroup; + children = ( + A3B082D21BB15CEA00D8DC35 /* CDVGestureHandler.h */, + A3B082D31BB15CEA00D8DC35 /* CDVGestureHandler.m */, + ); + path = CDVGestureHandler; + sourceTree = ""; + }; + C0C01EB31E3911D50056E6CB /* Cordova */ = { + isa = PBXGroup; + children = ( + C0C01EB41E3911D50056E6CB /* Cordova.h */, + C0C01EB51E3911D50056E6CB /* Info.plist */, + ); + path = Cordova; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + C0C01EAF1E3911D50056E6CB /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + C0C01EB61E3911D50056E6CB /* Cordova.h in Headers */, + C0C01EBB1E39131A0056E6CB /* CDV.h in Headers */, + C0C01EBC1E39131A0056E6CB /* CDVAppDelegate.h in Headers */, + C0C01EBD1E39131A0056E6CB /* CDVAvailability.h in Headers */, + C0C01EBE1E39131A0056E6CB /* CDVAvailabilityDeprecated.h in Headers */, + C0C01EBF1E39131A0056E6CB /* CDVCommandDelegate.h in Headers */, + C0C01EC01E39131A0056E6CB /* CDVCommandDelegateImpl.h in Headers */, + 4F56D839254A2EE40063F1D6 /* CDVWebViewProcessPoolFactory.h in Headers */, + C0C01EC11E39131A0056E6CB /* CDVCommandQueue.h in Headers */, + C0C01EC21E39131A0056E6CB /* CDVConfigParser.h in Headers */, + C0C01EC31E39131A0056E6CB /* CDVInvokedUrlCommand.h in Headers */, + C0C01EC41E39131A0056E6CB /* CDVPlugin+Resources.h in Headers */, + C0C01EC51E39131A0056E6CB /* CDVPlugin.h in Headers */, + C0C01EC61E39131A0056E6CB /* CDVPluginResult.h in Headers */, + 9059F51C26F2CE2400B3B2B7 /* CDVURLSchemeHandler.h in Headers */, + C0C01EC71E39131A0056E6CB /* CDVScreenOrientationDelegate.h in Headers */, + 4F56D836254A2EE10063F1D6 /* CDVWebViewEngine.h in Headers */, + C0C01EC81E39131A0056E6CB /* CDVTimer.h in Headers */, + C0C01ECB1E39131A0056E6CB /* CDVViewController.h in Headers */, + C0C01ECC1E39131A0056E6CB /* CDVWebViewEngineProtocol.h in Headers */, + C0C01ECD1E39131A0056E6CB /* CDVAllowList.h in Headers */, + C0C01ECE1E39131A0056E6CB /* NSDictionary+CordovaPreferences.h in Headers */, + 4E23F90323E17FFA006CD852 /* CDVWebViewUIDelegate.h in Headers */, + C0C01ECF1E39131A0056E6CB /* NSMutableArray+QueueAdditions.h in Headers */, + 9052DE892150D06B008E83D4 /* CDVDebug.h in Headers */, + 9052DE8A2150D06B008E83D4 /* CDVJSON_private.h in Headers */, + 9052DE8B2150D06B008E83D4 /* CDVPlugin+Private.h in Headers */, + 9052DE8C2150D06B008E83D4 /* CDVLogger.h in Headers */, + 9052DE8D2150D06B008E83D4 /* CDVGestureHandler.h in Headers */, + 9052DE8E2150D06B008E83D4 /* CDVIntentAndNavigationFilter.h in Headers */, + 9052DE8F2150D06B008E83D4 /* CDVHandleOpenURL.h in Headers */, + 2FCCEA18247E7366007276A8 /* CDVLaunchScreen.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D2AAC07A0554694100DB518D /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 7ED95D351AB9029B008C4574 /* CDV.h in Headers */, + 7ED95D361AB9029B008C4574 /* CDVAppDelegate.h in Headers */, + 7ED95D381AB9029B008C4574 /* CDVAvailability.h in Headers */, + 7ED95D391AB9029B008C4574 /* CDVAvailabilityDeprecated.h in Headers */, + 7ED95D3A1AB9029B008C4574 /* CDVCommandDelegate.h in Headers */, + 7ED95D3B1AB9029B008C4574 /* CDVCommandDelegateImpl.h in Headers */, + 7ED95D3D1AB9029B008C4574 /* CDVCommandQueue.h in Headers */, + 4E23F8FF23E16E96006CD852 /* CDVWebViewProcessPoolFactory.h in Headers */, + 7ED95D3F1AB9029B008C4574 /* CDVConfigParser.h in Headers */, + 2F4D42BC23F218BA00501999 /* CDVURLSchemeHandler.h in Headers */, + 7ED95D411AB9029B008C4574 /* CDVInvokedUrlCommand.h in Headers */, + 7ED95D431AB9029B008C4574 /* CDVPlugin+Resources.h in Headers */, + 7ED95D451AB9029B008C4574 /* CDVPlugin.h in Headers */, + 7ED95D471AB9029B008C4574 /* CDVPluginResult.h in Headers */, + 7ED95D491AB9029B008C4574 /* CDVScreenOrientationDelegate.h in Headers */, + 4E23F8FC23E16E96006CD852 /* CDVWebViewUIDelegate.h in Headers */, + 7ED95D4A1AB9029B008C4574 /* CDVTimer.h in Headers */, + 7ED95D501AB9029B008C4574 /* CDVViewController.h in Headers */, + 7ED95D521AB9029B008C4574 /* CDVWebViewEngineProtocol.h in Headers */, + 4E23F90023E16E96006CD852 /* CDVWebViewEngine.h in Headers */, + 7ED95D531AB9029B008C4574 /* CDVAllowList.h in Headers */, + 7ED95D571AB9029B008C4574 /* NSDictionary+CordovaPreferences.h in Headers */, + 7ED95D591AB9029B008C4574 /* NSMutableArray+QueueAdditions.h in Headers */, + 7ED95D021AB9028C008C4574 /* CDVDebug.h in Headers */, + 7ED95D031AB9028C008C4574 /* CDVJSON_private.h in Headers */, + 7ED95D051AB9028C008C4574 /* CDVPlugin+Private.h in Headers */, + 28BFF9141F355A4E00DDF01A /* CDVLogger.h in Headers */, + A3B082D41BB15CEA00D8DC35 /* CDVGestureHandler.h in Headers */, + 3093E2231B16D6A3003F381A /* CDVIntentAndNavigationFilter.h in Headers */, + 7E7F69B91ABA3692007546F4 /* CDVHandleOpenURL.h in Headers */, + 4E714D3623F535B500A321AF /* CDVLaunchScreen.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + C0C01EB11E3911D50056E6CB /* Cordova */ = { + isa = PBXNativeTarget; + buildConfigurationList = C0C01EB91E3911D50056E6CB /* Build configuration list for PBXNativeTarget "Cordova" */; + buildPhases = ( + C0C01EAF1E3911D50056E6CB /* Headers */, + C0C01EAD1E3911D50056E6CB /* Sources */, + C0C01EAE1E3911D50056E6CB /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Cordova; + productName = Cordova; + productReference = C0C01EB21E3911D50056E6CB /* Cordova.framework */; + productType = "com.apple.product-type.framework"; + }; + D2AAC07D0554694100DB518D /* CordovaLib */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "CordovaLib" */; + buildPhases = ( + D2AAC07A0554694100DB518D /* Headers */, + D2AAC07B0554694100DB518D /* Sources */, + D2AAC07C0554694100DB518D /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CordovaLib; + productName = CordovaLib; + productReference = 68A32D7114102E1C006B237C /* libCordova.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0867D690FE84028FC02AAC07 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1010; + TargetAttributes = { + C0C01EB11E3911D50056E6CB = { + CreatedOnToolsVersion = 10.1; + ProvisioningStyle = Automatic; + }; + D2AAC07D0554694100DB518D = { + CreatedOnToolsVersion = 10.1; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "CordovaLib" */; + compatibilityVersion = "Xcode 11.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 0867D691FE84028FC02AAC07; + productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D2AAC07D0554694100DB518D /* CordovaLib */, + C0C01EB11E3911D50056E6CB /* Cordova */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + C0C01EAD1E3911D50056E6CB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9052DE712150D040008E83D4 /* CDVAppDelegate.m in Sources */, + 9052DE722150D040008E83D4 /* CDVCommandDelegateImpl.m in Sources */, + 9052DE732150D040008E83D4 /* CDVCommandQueue.m in Sources */, + 9052DE742150D040008E83D4 /* CDVConfigParser.m in Sources */, + 9052DE752150D040008E83D4 /* CDVInvokedUrlCommand.m in Sources */, + 9052DE762150D040008E83D4 /* CDVPlugin+Resources.m in Sources */, + 9052DE772150D040008E83D4 /* CDVPlugin.m in Sources */, + 9052DE782150D040008E83D4 /* CDVPluginResult.m in Sources */, + 4F56D830254A2ED70063F1D6 /* CDVWebViewUIDelegate.m in Sources */, + 9052DE792150D040008E83D4 /* CDVTimer.m in Sources */, + 4F56D833254A2ED90063F1D6 /* CDVWebViewProcessPoolFactory.m in Sources */, + 4F56D82D254A2EB50063F1D6 /* CDVWebViewEngine.m in Sources */, + 9052DE7C2150D040008E83D4 /* CDVViewController.m in Sources */, + 4F56D83C254A2F2F0063F1D6 /* CDVURLSchemeHandler.m in Sources */, + 9052DE7D2150D040008E83D4 /* CDVAllowList.m in Sources */, + 9052DE7E2150D040008E83D4 /* NSDictionary+CordovaPreferences.m in Sources */, + 9052DE7F2150D040008E83D4 /* NSMutableArray+QueueAdditions.m in Sources */, + 9052DE802150D040008E83D4 /* CDVJSON_private.m in Sources */, + 9052DE812150D040008E83D4 /* CDVLogger.m in Sources */, + 9052DE822150D040008E83D4 /* CDVGestureHandler.m in Sources */, + 9052DE832150D040008E83D4 /* CDVIntentAndNavigationFilter.m in Sources */, + 9052DE842150D040008E83D4 /* CDVHandleOpenURL.m in Sources */, + 2FCCEA17247E7366007276A8 /* CDVLaunchScreen.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D2AAC07B0554694100DB518D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7ED95D371AB9029B008C4574 /* CDVAppDelegate.m in Sources */, + 7ED95D3C1AB9029B008C4574 /* CDVCommandDelegateImpl.m in Sources */, + 7ED95D3E1AB9029B008C4574 /* CDVCommandQueue.m in Sources */, + 7ED95D401AB9029B008C4574 /* CDVConfigParser.m in Sources */, + 7ED95D421AB9029B008C4574 /* CDVInvokedUrlCommand.m in Sources */, + 7ED95D441AB9029B008C4574 /* CDVPlugin+Resources.m in Sources */, + 2F4D42BD23F218BA00501999 /* CDVURLSchemeHandler.m in Sources */, + 7ED95D461AB9029B008C4574 /* CDVPlugin.m in Sources */, + 7ED95D481AB9029B008C4574 /* CDVPluginResult.m in Sources */, + 7ED95D4B1AB9029B008C4574 /* CDVTimer.m in Sources */, + 4E23F8FE23E16E96006CD852 /* CDVWebViewEngine.m in Sources */, + 4E23F8FB23E16E96006CD852 /* CDVWebViewProcessPoolFactory.m in Sources */, + 7ED95D511AB9029B008C4574 /* CDVViewController.m in Sources */, + 7ED95D541AB9029B008C4574 /* CDVAllowList.m in Sources */, + 7ED95D581AB9029B008C4574 /* NSDictionary+CordovaPreferences.m in Sources */, + 7ED95D5A1AB9029B008C4574 /* NSMutableArray+QueueAdditions.m in Sources */, + 7ED95D041AB9028C008C4574 /* CDVJSON_private.m in Sources */, + 4E23F8FD23E16E96006CD852 /* CDVWebViewUIDelegate.m in Sources */, + 28BFF9151F355A4E00DDF01A /* CDVLogger.m in Sources */, + A3B082D51BB15CEA00D8DC35 /* CDVGestureHandler.m in Sources */, + 3093E2241B16D6A3003F381A /* CDVIntentAndNavigationFilter.m in Sources */, + 7ED95D071AB9028C008C4574 /* CDVHandleOpenURL.m in Sources */, + 4E714D3823F535B500A321AF /* CDVLaunchScreen.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1DEB921F08733DC00010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ENABLE_MODULE_VERIFIER = YES; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++14"; + PUBLIC_HEADERS_FOLDER_PATH = include/Cordova; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 1DEB922008733DC00010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ENABLE_MODULE_VERIFIER = YES; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++14"; + PUBLIC_HEADERS_FOLDER_PATH = include/Cordova; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 1DEB922308733DC00010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MERGEABLE_LIBRARY = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = Cordova; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 1DEB922408733DC00010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = ""; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MERGEABLE_LIBRARY = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = Cordova; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + C0C01EB71E3911D50056E6CB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + INFOPLIST_FILE = Cordova/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++14"; + PRODUCT_BUNDLE_IDENTIFIER = org.apache.cordova.Cordova; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + C0C01EB81E3911D50056E6CB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + INFOPLIST_FILE = Cordova/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++14"; + PRODUCT_BUNDLE_IDENTIFIER = org.apache.cordova.Cordova; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "CordovaLib" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB921F08733DC00010E9CD /* Debug */, + 1DEB922008733DC00010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "CordovaLib" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB922308733DC00010E9CD /* Debug */, + 1DEB922408733DC00010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C0C01EB91E3911D50056E6CB /* Build configuration list for PBXNativeTarget "Cordova" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C0C01EB71E3911D50056E6CB /* Debug */, + C0C01EB81E3911D50056E6CB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0867D690FE84028FC02AAC07 /* Project object */; +} diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDV.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDV.h new file mode 100644 index 00000000..2002ac8a --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDV.h @@ -0,0 +1,30 @@ +/* + 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. + */ + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVAllowList.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVAllowList.h new file mode 100644 index 00000000..57814b1e --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVAllowList.h @@ -0,0 +1,34 @@ +/* + 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. + */ + +#import + +extern NSString* const kCDVDefaultAllowListRejectionString; + +@interface CDVAllowList : NSObject + +@property (nonatomic, copy) NSString* allowListRejectionFormatString; + +- (id)initWithArray:(NSArray*)array; +- (BOOL)schemeIsAllowed:(NSString*)scheme; +- (BOOL)URLIsAllowed:(NSURL*)url; +- (BOOL)URLIsAllowed:(NSURL*)url logFailure:(BOOL)logFailure; +- (NSString*)errorStringForURL:(NSURL*)url; + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVAppDelegate.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVAppDelegate.h new file mode 100644 index 00000000..e2cd85d8 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVAppDelegate.h @@ -0,0 +1,28 @@ +/* + 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. + */ + +#import +#import + +@interface CDVAppDelegate : UIResponder + +@property (nullable, nonatomic, strong) IBOutlet UIWindow* window; +@property (nullable, nonatomic, strong) IBOutlet CDVViewController* viewController; + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVAvailability.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVAvailability.h new file mode 100644 index 00000000..5f6d889f --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVAvailability.h @@ -0,0 +1,121 @@ +/* + 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. + */ + +#import + +#define __CORDOVA_IOS__ + +#define __CORDOVA_0_9_6 906 +#define __CORDOVA_1_0_0 10000 +#define __CORDOVA_1_1_0 10100 +#define __CORDOVA_1_2_0 10200 +#define __CORDOVA_1_3_0 10300 +#define __CORDOVA_1_4_0 10400 +#define __CORDOVA_1_4_1 10401 +#define __CORDOVA_1_5_0 10500 +#define __CORDOVA_1_6_0 10600 +#define __CORDOVA_1_6_1 10601 +#define __CORDOVA_1_7_0 10700 +#define __CORDOVA_1_8_0 10800 +#define __CORDOVA_1_8_1 10801 +#define __CORDOVA_1_9_0 10900 +#define __CORDOVA_2_0_0 20000 +#define __CORDOVA_2_1_0 20100 +#define __CORDOVA_2_2_0 20200 +#define __CORDOVA_2_3_0 20300 +#define __CORDOVA_2_4_0 20400 +#define __CORDOVA_2_5_0 20500 +#define __CORDOVA_2_6_0 20600 +#define __CORDOVA_2_7_0 20700 +#define __CORDOVA_2_8_0 20800 +#define __CORDOVA_2_9_0 20900 +#define __CORDOVA_3_0_0 30000 +#define __CORDOVA_3_1_0 30100 +#define __CORDOVA_3_2_0 30200 +#define __CORDOVA_3_3_0 30300 +#define __CORDOVA_3_4_0 30400 +#define __CORDOVA_3_4_1 30401 +#define __CORDOVA_3_5_0 30500 +#define __CORDOVA_3_6_0 30600 +#define __CORDOVA_3_7_0 30700 +#define __CORDOVA_3_8_0 30800 +#define __CORDOVA_3_9_0 30900 +#define __CORDOVA_3_9_1 30901 +#define __CORDOVA_3_9_2 30902 +#define __CORDOVA_4_0_0 40000 +#define __CORDOVA_4_0_1 40001 +#define __CORDOVA_4_1_0 40100 +#define __CORDOVA_4_1_1 40101 +#define __CORDOVA_4_2_0 40200 +#define __CORDOVA_4_2_1 40201 +#define __CORDOVA_4_3_0 40300 +#define __CORDOVA_4_3_1 40301 +#define __CORDOVA_4_4_0 40400 +#define __CORDOVA_4_5_0 40500 +#define __CORDOVA_4_5_1 40501 +#define __CORDOVA_4_5_2 40502 +#define __CORDOVA_4_5_4 40504 +#define __CORDOVA_5_0_0 50000 +#define __CORDOVA_5_0_1 50001 +#define __CORDOVA_5_1_0 50100 +#define __CORDOVA_5_1_1 50101 +#define __CORDOVA_6_0_0 60000 +#define __CORDOVA_6_1_0 60100 +#define __CORDOVA_6_2_0 60200 +#define __CORDOVA_6_3_0 60300 +#define __CORDOVA_7_0_0 70000 +#define __CORDOVA_7_0_1 70001 +/* coho:next-version,insert-before */ +#define __CORDOVA_NA 99999 /* not available */ + +/* + #if CORDOVA_VERSION_MIN_REQUIRED >= __CORDOVA_4_0_0 + // do something when its at least 4.0.0 + #else + // do something else (non 4.0.0) + #endif + */ +#ifndef CORDOVA_VERSION_MIN_REQUIRED + /* coho:next-version-min-required,replace-after */ + #define CORDOVA_VERSION_MIN_REQUIRED __CORDOVA_7_0_1 +#endif + +/* + Returns YES if it is at least version specified as NSString(X) + Usage: + if (IsAtLeastiOSVersion(@"5.1")) { + // do something for iOS 5.1 or greater + } + */ +#define IsAtLeastiOSVersion(X) ([[[UIDevice currentDevice] systemVersion] compare:X options:NSNumericSearch] != NSOrderedAscending) + +/* Return the string version of the decimal version */ +#define CDV_VERSION [NSString stringWithFormat:@"%d.%d.%d", \ + (CORDOVA_VERSION_MIN_REQUIRED / 10000), \ + (CORDOVA_VERSION_MIN_REQUIRED % 10000) / 100, \ + (CORDOVA_VERSION_MIN_REQUIRED % 10000) % 100] + +// Enable this to log all exec() calls. +#define CDV_ENABLE_EXEC_LOGGING 0 +#if CDV_ENABLE_EXEC_LOGGING + #define CDV_EXEC_LOG NSLog +#else + #define CDV_EXEC_LOG(...) do { \ +} while (NO) +#endif diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVAvailabilityDeprecated.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVAvailabilityDeprecated.h new file mode 100644 index 00000000..abf7a16a --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVAvailabilityDeprecated.h @@ -0,0 +1,26 @@ +/* + 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. + */ + +#import + +#ifdef __clang__ + #define CDV_DEPRECATED(version, msg) __attribute__((deprecated("Deprecated in Cordova " #version ". " msg))) +#else + #define CDV_DEPRECATED(version, msg) __attribute__((deprecated())) +#endif diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVCommandDelegate.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVCommandDelegate.h new file mode 100644 index 00000000..76b95231 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVCommandDelegate.h @@ -0,0 +1,49 @@ +/* + 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. + */ + +#import +#import + +@class CDVPlugin; +@class CDVPluginResult; +@class CDVAllowList; + +typedef NSURL* (^ UrlTransformerBlock)(NSURL*); + +@protocol CDVCommandDelegate + +@property (nonatomic, readonly) NSDictionary* settings; +@property (nonatomic, copy) UrlTransformerBlock urlTransformer; + +- (NSString*)pathForResource:(NSString*)resourcepath; +- (id)getCommandInstance:(NSString*)pluginName; + +// Sends a plugin result to the JS. This is thread-safe. +- (void)sendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId; +// Evaluates the given JS. This is thread-safe. +- (void)evalJs:(NSString*)js; +// Can be used to evaluate JS right away instead of scheduling it on the run-loop. +// This is required for dispatch resign and pause events, but should not be used +// without reason. Without the run-loop delay, alerts used in JS callbacks may result +// in dead-lock. This method must be called from the UI thread. +- (void)evalJs:(NSString*)js scheduledOnRunLoop:(BOOL)scheduledOnRunLoop; +// Runs the given block on a background thread using a shared thread-pool. +- (void)runInBackground:(void (^)(void))block; + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVCommandQueue.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVCommandQueue.h new file mode 100644 index 00000000..cb7bd6e4 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVCommandQueue.h @@ -0,0 +1,39 @@ +/* + 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. + */ + +#import + +@class CDVInvokedUrlCommand; +@class CDVViewController; + +@interface CDVCommandQueue : NSObject + +@property (nonatomic, readonly) BOOL currentlyExecuting; + +- (id)initWithViewController:(CDVViewController*)viewController; +- (void)dispose; + +- (void)resetRequestId; +- (void)enqueueCommandBatch:(NSString*)batchJSON; + +- (void)fetchCommandsFromJs; +- (void)executePending; +- (BOOL)execute:(CDVInvokedUrlCommand*)command; + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVConfigParser.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVConfigParser.h new file mode 100644 index 00000000..0a937031 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVConfigParser.h @@ -0,0 +1,32 @@ +/* + 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. + */ + +#import + +@interface CDVConfigParser : NSObject +{ + NSString* featureName; +} + +@property (nonatomic, readonly, strong) NSMutableDictionary* pluginsDict; +@property (nonatomic, readonly, strong) NSMutableDictionary* settings; +@property (nonatomic, readonly, strong) NSMutableArray* startupPluginNames; +@property (nonatomic, readonly, strong) NSString* startPage; + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVInvokedUrlCommand.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVInvokedUrlCommand.h new file mode 100644 index 00000000..993e0a28 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVInvokedUrlCommand.h @@ -0,0 +1,52 @@ +/* + 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. + */ + +#import + +@interface CDVInvokedUrlCommand : NSObject { + NSString* _callbackId; + NSString* _className; + NSString* _methodName; + NSArray* _arguments; +} + +@property (nonatomic, readonly) NSArray* arguments; +@property (nonatomic, readonly) NSString* callbackId; +@property (nonatomic, readonly) NSString* className; +@property (nonatomic, readonly) NSString* methodName; + ++ (CDVInvokedUrlCommand*)commandFromJson:(NSArray*)jsonEntry; + +- (id)initWithArguments:(NSArray*)arguments + callbackId:(NSString*)callbackId + className:(NSString*)className + methodName:(NSString*)methodName; + +- (id)initFromJson:(NSArray*)jsonEntry; + +// Returns the argument at the given index. +// If index >= the number of arguments, returns nil. +// If the argument at the given index is NSNull, returns nil. +- (id)argumentAtIndex:(NSUInteger)index; +// Same as above, but returns defaultValue instead of nil. +- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue; +// Same as above, but returns defaultValue instead of nil, and if the argument is not of the expected class, returns defaultValue +- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue andClass:(Class)aClass; + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVPlugin+Resources.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVPlugin+Resources.h new file mode 100644 index 00000000..178ecb3a --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVPlugin+Resources.h @@ -0,0 +1,39 @@ +/* + 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. + */ + +#import +#import + +@interface CDVPlugin (CDVPluginResources) + +/* + This will return the localized string for a key in a .bundle that is named the same as your class + For example, if your plugin class was called Foo, and you have a Spanish localized strings file, it will + try to load the desired key from Foo.bundle/es.lproj/Localizable.strings + */ +- (NSString*)pluginLocalizedString:(NSString*)key; + +/* + This will return the image for a name in a .bundle that is named the same as your class + For example, if your plugin class was called Foo, and you have an image called "bar", + it will try to load the image from Foo.bundle/bar.png (and appropriately named retina versions) + */ +- (UIImage*)pluginImageResource:(NSString*)name; + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVPlugin.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVPlugin.h new file mode 100644 index 00000000..a29b34c7 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVPlugin.h @@ -0,0 +1,74 @@ +/* + 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. + */ + +#import +#import +#import +#import +#import +#import + +@interface UIView (org_apache_cordova_UIView_Extension) + +@property (nonatomic, weak) UIScrollView* scrollView; + +@end + +extern NSString* const CDVPageDidLoadNotification; +extern NSString* const CDVPluginHandleOpenURLNotification; +extern NSString* const CDVPluginHandleOpenURLWithAppSourceAndAnnotationNotification; +extern NSString* const CDVPluginResetNotification; +extern NSString* const CDVViewWillAppearNotification; +extern NSString* const CDVViewDidAppearNotification; +extern NSString* const CDVViewWillDisappearNotification; +extern NSString* const CDVViewDidDisappearNotification; +extern NSString* const CDVViewWillLayoutSubviewsNotification; +extern NSString* const CDVViewDidLayoutSubviewsNotification; +extern NSString* const CDVViewWillTransitionToSizeNotification; + +@interface CDVPlugin : NSObject {} + +@property (nonatomic, readonly, weak) UIView* webView; +@property (nonatomic, readonly, weak) id webViewEngine; + +@property (nonatomic, weak) UIViewController* viewController; +@property (nonatomic, weak) id commandDelegate; + +@property (readonly, assign) BOOL hasPendingOperation; + +- (void)pluginInitialize; + +- (void)handleOpenURL:(NSNotification*)notification; +- (void)handleOpenURLWithApplicationSourceAndAnnotation:(NSNotification*)notification; +- (void)onAppTerminate; +- (void)onMemoryWarning; +- (void)onReset; +- (void)dispose; + +/* + // see initWithWebView implementation + - (void) onPause {} + - (void) onResume {} + - (void) onOrientationWillChange {} + - (void) onOrientationDidChange {} + */ + +- (id)appDelegate; + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVPluginResult.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVPluginResult.h new file mode 100644 index 00000000..39b56e85 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVPluginResult.h @@ -0,0 +1,83 @@ +/* + 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. + */ + +#import +#import + +typedef NS_ENUM(NSUInteger, CDVCommandStatus) { + CDVCommandStatus_NO_RESULT NS_SWIFT_NAME(noResult) = 0, + CDVCommandStatus_OK NS_SWIFT_NAME(ok), + CDVCommandStatus_CLASS_NOT_FOUND_EXCEPTION NS_SWIFT_NAME(classNotFoundException), + CDVCommandStatus_ILLEGAL_ACCESS_EXCEPTION NS_SWIFT_NAME(illegalAccessException), + CDVCommandStatus_INSTANTIATION_EXCEPTION NS_SWIFT_NAME(instantiationException), + CDVCommandStatus_MALFORMED_URL_EXCEPTION NS_SWIFT_NAME(malformedUrlException), + CDVCommandStatus_IO_EXCEPTION NS_SWIFT_NAME(ioException), + CDVCommandStatus_INVALID_ACTION NS_SWIFT_NAME(invalidAction), + CDVCommandStatus_JSON_EXCEPTION NS_SWIFT_NAME(jsonException), + CDVCommandStatus_ERROR NS_SWIFT_NAME(error) +}; + +// This exists to preserve compatibility with early Swift plugins, who are +// using CDVCommandStatus as ObjC-style constants rather than as Swift enum +// values. +// This declares extern'ed constants (implemented in CDVPluginResult.m) +#define SWIFT_ENUM_COMPAT_HACK(enumVal) extern const CDVCommandStatus SWIFT_##enumVal NS_SWIFT_NAME(enumVal) +SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_NO_RESULT); +SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_OK); +SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_CLASS_NOT_FOUND_EXCEPTION); +SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_ILLEGAL_ACCESS_EXCEPTION); +SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_INSTANTIATION_EXCEPTION); +SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_MALFORMED_URL_EXCEPTION); +SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_IO_EXCEPTION); +SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_INVALID_ACTION); +SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_JSON_EXCEPTION); +SWIFT_ENUM_COMPAT_HACK(CDVCommandStatus_ERROR); +#undef SWIFT_ENUM_COMPAT_HACK + +@interface CDVPluginResult : NSObject {} + +@property (nonatomic, strong, readonly) NSNumber* status; +@property (nonatomic, strong, readonly) id message; +@property (nonatomic, strong) NSNumber* keepCallback; +// This property can be used to scope the lifetime of another object. For example, +// Use it to store the associated NSData when `message` is created using initWithBytesNoCopy. +@property (nonatomic, strong) id associatedObject; + +- (CDVPluginResult*)init; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsString:(NSString*)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArray:(NSArray*)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsInt:(int)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsNSInteger:(NSInteger)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsNSUInteger:(NSUInteger)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDouble:(double)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsBool:(BOOL)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDictionary:(NSDictionary*)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsMultipart:(NSArray*)theMessages; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode; + ++ (void)setVerbose:(BOOL)verbose; ++ (BOOL)isVerbose; + +- (void)setKeepCallbackAsBool:(BOOL)bKeepCallback; + +- (NSString*)argumentsAsJSON; + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVScreenOrientationDelegate.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVScreenOrientationDelegate.h new file mode 100644 index 00000000..49c3c270 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVScreenOrientationDelegate.h @@ -0,0 +1,28 @@ +/* + 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. + */ + +#import + +@protocol CDVScreenOrientationDelegate + +- (UIInterfaceOrientationMask)supportedInterfaceOrientations; + +- (BOOL)shouldAutorotate; + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVTimer.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVTimer.h new file mode 100644 index 00000000..6d31593f --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVTimer.h @@ -0,0 +1,27 @@ +/* + 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. + */ + +#import + +@interface CDVTimer : NSObject + ++ (void)start:(NSString*)name; ++ (void)stop:(NSString*)name; + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVURLSchemeHandler.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVURLSchemeHandler.h new file mode 100644 index 00000000..f0440ab2 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVURLSchemeHandler.h @@ -0,0 +1,34 @@ +/* + 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. + */ + +#import +#import +#import + + +@interface CDVURLSchemeHandler : NSObject + +@property (nonatomic, weak) CDVViewController* viewController; + +@property (nonatomic) CDVPlugin* schemePlugin; + +- (instancetype)initWithVC:(CDVViewController *)controller; + + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVViewController.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVViewController.h new file mode 100644 index 00000000..84295051 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVViewController.h @@ -0,0 +1,100 @@ +/* + 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. + */ + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +@protocol CDVWebViewEngineConfigurationDelegate + +@optional +/// Provides a fully configured WKWebViewConfiguration which will be overriden with +/// any related settings you add to config.xml (e.g., `PreferredContentMode`). +/// Useful for more complex configuration, including websiteDataStore. +/// +/// Example usage: +/// +/// extension CDVViewController: CDVWebViewEngineConfigurationDelegate { +/// public func configuration() -> WKWebViewConfiguration { +/// // return your config here +/// } +/// } +- (nonnull WKWebViewConfiguration*)configuration; + +@end + +@interface CDVViewController : UIViewController { + @protected + id _webViewEngine; + @protected + id _commandDelegate; + @protected + CDVCommandQueue* _commandQueue; +} + +NS_ASSUME_NONNULL_BEGIN + +@property (nonatomic, readonly, weak) IBOutlet UIView* webView; +@property (nonatomic, readonly, strong) UIView* launchView; + +@property (nullable, nonatomic, readonly, strong) NSMutableDictionary* pluginObjects; +@property (nonatomic, readonly, strong) NSDictionary* pluginsMap; +@property (nonatomic, readonly, strong) NSMutableDictionary* settings; +@property (nonatomic, readonly, strong) NSXMLParser* configParser; + +@property (nonatomic, readwrite, copy) NSString* appScheme; +@property (nonatomic, readwrite, copy) NSString* configFile; +@property (nonatomic, readwrite, copy) NSString* wwwFolderName; +@property (nonatomic, readwrite, copy) NSString* startPage; +@property (nonatomic, readonly, strong) CDVCommandQueue* commandQueue; +@property (nonatomic, readonly, strong) id webViewEngine; +@property (nonatomic, readonly, strong) id commandDelegate; + +/** + Takes/Gives an array of UIInterfaceOrientation (int) objects + ex. UIInterfaceOrientationPortrait +*/ +@property (nonatomic, readwrite, strong) NSArray* supportedOrientations; + +- (UIView*)newCordovaViewWithFrame:(CGRect)bounds; + +- (nullable NSString*)appURLScheme; +- (nullable NSURL*)errorURL; + +- (NSArray*)parseInterfaceOrientations:(NSArray*)orientations; +- (BOOL)supportsOrientation:(UIInterfaceOrientation)orientation; + +- (nullable id)getCommandInstance:(NSString*)pluginName; +- (void)registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className; +- (void)registerPlugin:(CDVPlugin*)plugin withPluginName:(NSString*)pluginName; + +- (void)parseSettingsWithParser:(NSObject *)delegate; + +- (void)showLaunchScreen:(BOOL)visible; + +NS_ASSUME_NONNULL_END + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVWebViewEngineProtocol.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVWebViewEngineProtocol.h new file mode 100644 index 00000000..40187bca --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVWebViewEngineProtocol.h @@ -0,0 +1,51 @@ +/* + 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. + */ + +#import +#import + +#define kCDVWebViewEngineScriptMessageHandlers @"kCDVWebViewEngineScriptMessageHandlers" +#define kCDVWebViewEngineWKNavigationDelegate @"kCDVWebViewEngineWKNavigationDelegate" +#define kCDVWebViewEngineWKUIDelegate @"kCDVWebViewEngineWKUIDelegate" +#define kCDVWebViewEngineWebViewPreferences @"kCDVWebViewEngineWebViewPreferences" + +@protocol CDVWebViewEngineProtocol + +NS_ASSUME_NONNULL_BEGIN + +@property (nonatomic, strong, readonly) UIView* engineWebView; + +- (id)loadRequest:(NSURLRequest*)request; +- (id)loadHTMLString:(NSString*)string baseURL:(nullable NSURL*)baseURL; +- (void)evaluateJavaScript:(NSString*)javaScriptString completionHandler:(void (^_Nullable)(id, NSError*))completionHandler; + +- (NSURL*)URL; +- (BOOL)canLoadRequest:(NSURLRequest*)request; +- (nullable instancetype)initWithFrame:(CGRect)frame; + +/// Convenience Initializer +/// @param frame The frame for the new web view. +/// @param configuration The configuration for the new web view. +- (nullable instancetype)initWithFrame:(CGRect)frame configuration:(nullable WKWebViewConfiguration *)configuration; + +- (void)updateWithInfo:(NSDictionary*)info; + +NS_ASSUME_NONNULL_END + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVWebViewProcessPoolFactory.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVWebViewProcessPoolFactory.h new file mode 100644 index 00000000..b1562891 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/CDVWebViewProcessPoolFactory.h @@ -0,0 +1,27 @@ +/* + 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. + */ + +#import + +@interface CDVWebViewProcessPoolFactory : NSObject +@property (nonatomic, retain) WKProcessPool* sharedPool; + ++(instancetype) sharedFactory; +-(WKProcessPool*) sharedProcessPool; +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/NSDictionary+CordovaPreferences.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/NSDictionary+CordovaPreferences.h new file mode 100644 index 00000000..9be2be2d --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/NSDictionary+CordovaPreferences.h @@ -0,0 +1,35 @@ +/* + 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. + */ + +#import +#import + +@interface NSDictionary (CordovaPreferences) + +- (id)cordovaSettingForKey:(NSString*)key; +- (BOOL)cordovaBoolSettingForKey:(NSString*)key defaultValue:(BOOL)defaultValue; +- (CGFloat)cordovaFloatSettingForKey:(NSString*)key defaultValue:(CGFloat)defaultValue; + +@end + +@interface NSMutableDictionary (CordovaPreferences) + +- (void)setCordovaSetting:(id)value forKey:(NSString*)key; + +@end diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/NSMutableArray+QueueAdditions.h b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/NSMutableArray+QueueAdditions.h new file mode 100644 index 00000000..79e65164 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/include/Cordova/NSMutableArray+QueueAdditions.h @@ -0,0 +1,29 @@ +/* + 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. + */ + +#import + +@interface NSMutableArray (QueueAdditions) + +- (id)cdv_pop; +- (id)cdv_queueHead; +- (id)cdv_dequeue; +- (void)cdv_enqueue:(id)obj; + +@end diff --git a/MovieVerse-Mobile/platforms/ios/MovieVerse.xcodeproj/project.pbxproj b/MovieVerse-Mobile/platforms/ios/MovieVerse.xcodeproj/project.pbxproj new file mode 100755 index 00000000..34b2a8a1 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/MovieVerse.xcodeproj/project.pbxproj @@ -0,0 +1,465 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 52; + objects = { + +/* Begin PBXBuildFile section */ + 0207DA581B56EA530066E2B4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0207DA571B56EA530066E2B4 /* Assets.xcassets */; }; + 1D3623260D0F684500981E51 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* AppDelegate.m */; }; + 1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; }; + 301BF552109A68D80062928A /* libCordova.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF535109A57CC0062928A /* libCordova.a */; settings = {ATTRIBUTES = (Required, ); }; }; + 302D95F114D2391D003F00A1 /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 302D95EF14D2391D003F00A1 /* MainViewController.m */; }; + 302D95F214D2391D003F00A1 /* MainViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 302D95F014D2391D003F00A1 /* MainViewController.xib */; }; + 4E7CA2B6272ABB0D00177EF9 /* config.xml in Copy Staging Resources */ = {isa = PBXBuildFile; fileRef = F840E1F0165FE0F500CFE078 /* config.xml */; }; + 4E7CA2B7272ABB0D00177EF9 /* www in Copy Staging Resources */ = {isa = PBXBuildFile; fileRef = 301BF56E109A69640062928A /* www */; }; + 6AFF5BF91D6E424B00AB3073 /* CDVLaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6AFF5BF81D6E424B00AB3073 /* CDVLaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 301BF534109A57CC0062928A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 301BF52D109A57CC0062928A /* CordovaLib/CordovaLib.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = D2AAC07E0554694100DB518D; + remoteInfo = CordovaLib; + }; + 301BF550109A68C00062928A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 301BF52D109A57CC0062928A /* CordovaLib/CordovaLib.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = D2AAC07D0554694100DB518D; + remoteInfo = CordovaLib; + }; + 907D8123214C687600058A10 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 301BF52D109A57CC0062928A /* CordovaLib/CordovaLib.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = C0C01EB21E3911D50056E6CB; + remoteInfo = Cordova; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 857339E32710CC9700A1C74C /* Copy Staging Resources */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 7; + files = ( + 4E7CA2B6272ABB0D00177EF9 /* config.xml in Copy Staging Resources */, + 4E7CA2B7272ABB0D00177EF9 /* www in Copy Staging Resources */, + ); + name = "Copy Staging Resources"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0207DA571B56EA530066E2B4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 1D3623240D0F684500981E51 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 1D3623250D0F684500981E51 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 1D6058910D05DD3D006BFB54 /* MovieVerse.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "MovieVerse.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 301BF52D109A57CC0062928A /* CordovaLib/CordovaLib.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CordovaLib.xcodeproj; path = CordovaLib/CordovaLib.xcodeproj; sourceTree = ""; }; + 301BF56E109A69640062928A /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; path = www; sourceTree = SOURCE_ROOT; }; + 302D95EE14D2391D003F00A1 /* MainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = ""; }; + 302D95EF14D2391D003F00A1 /* MainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainViewController.m; sourceTree = ""; }; + 302D95F014D2391D003F00A1 /* MainViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainViewController.xib; sourceTree = ""; }; + 3047A50F1AB8059700498E2A /* build-debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "build-debug.xcconfig"; path = "cordova/build-debug.xcconfig"; sourceTree = SOURCE_ROOT; }; + 3047A5101AB8059700498E2A /* build-release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "build-release.xcconfig"; path = "cordova/build-release.xcconfig"; sourceTree = SOURCE_ROOT; }; + 3047A5111AB8059700498E2A /* build.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = build.xcconfig; path = cordova/build.xcconfig; sourceTree = SOURCE_ROOT; }; + 32CA4F630368D1EE00C91783 /* MovieVerse-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MovieVerse-Prefix.pch"; sourceTree = ""; }; + 6AFF5BF81D6E424B00AB3073 /* CDVLaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = CDVLaunchScreen.storyboard; sourceTree = ""; }; + 8D1107310486CEB800E47090 /* MovieVerse-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "MovieVerse-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = ""; }; + EB87FDF31871DA8E0020F90C /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; name = www; path = ../../www; sourceTree = ""; }; + EB87FDF41871DAF40020F90C /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = config.xml; path = ../../config.xml; sourceTree = ""; }; + ED33DF2A687741AEAF9F8254 /* Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = ""; }; + F840E1F0165FE0F500CFE078 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = config.xml; path = "MovieVerse/config.xml"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1D60588F0D05DD3D006BFB54 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 301BF552109A68D80062928A /* libCordova.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 1D6058910D05DD3D006BFB54 /* MovieVerse.app */, + ); + name = Products; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { + isa = PBXGroup; + children = ( + EB87FDF41871DAF40020F90C /* config.xml */, + 3047A50E1AB8057F00498E2A /* config */, + EB87FDF31871DA8E0020F90C /* www */, + EB87FDF11871DA420020F90C /* Staging */, + 301BF52D109A57CC0062928A /* CordovaLib/CordovaLib.xcodeproj */, + 29B97315FDCFA39411CA2CEA /* MovieVerse */, + 307C750510C5A3420062BCA9 /* Plugins */, + 29B97317FDCFA39411CA2CEA /* Resources */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = CustomTemplate; + sourceTree = ""; + }; + 29B97315FDCFA39411CA2CEA /* MovieVerse */ = { + isa = PBXGroup; + children = ( + 8D1107310486CEB800E47090 /* MovieVerse-Info.plist */, + 32CA4F630368D1EE00C91783 /* MovieVerse-Prefix.pch */, + 6AFF5BF81D6E424B00AB3073 /* CDVLaunchScreen.storyboard */, + 0207DA571B56EA530066E2B4 /* Assets.xcassets */, + ED33DF2A687741AEAF9F8254 /* Bridging-Header.h */, + 302D95EE14D2391D003F00A1 /* MainViewController.h */, + 302D95EF14D2391D003F00A1 /* MainViewController.m */, + 302D95F014D2391D003F00A1 /* MainViewController.xib */, + 1D3623240D0F684500981E51 /* AppDelegate.h */, + 1D3623250D0F684500981E51 /* AppDelegate.m */, + 29B97316FDCFA39411CA2CEA /* main.m */, + ); + name = "MovieVerse"; + path = "MovieVerse"; + sourceTree = ""; + }; + 29B97317FDCFA39411CA2CEA /* Resources */ = { + isa = PBXGroup; + children = ( + ); + name = Resources; + path = "MovieVerse/Resources"; + sourceTree = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + 301BF52E109A57CC0062928A /* Products */ = { + isa = PBXGroup; + children = ( + 301BF535109A57CC0062928A /* libCordova.a */, + 907D8124214C687600058A10 /* Cordova.framework */, + ); + name = Products; + sourceTree = ""; + }; + 3047A50E1AB8057F00498E2A /* config */ = { + isa = PBXGroup; + children = ( + 3047A50F1AB8059700498E2A /* build-debug.xcconfig */, + 3047A5101AB8059700498E2A /* build-release.xcconfig */, + 3047A5111AB8059700498E2A /* build.xcconfig */, + ); + name = config; + sourceTree = ""; + }; + 307C750510C5A3420062BCA9 /* Plugins */ = { + isa = PBXGroup; + children = ( + ); + name = Plugins; + path = "MovieVerse/Plugins"; + sourceTree = SOURCE_ROOT; + }; + EB87FDF11871DA420020F90C /* Staging */ = { + isa = PBXGroup; + children = ( + F840E1F0165FE0F500CFE078 /* config.xml */, + 301BF56E109A69640062928A /* www */, + ); + name = Staging; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1D6058900D05DD3D006BFB54 /* MovieVerse */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "MovieVerse" */; + buildPhases = ( + 857339E32710CC9700A1C74C /* Copy Staging Resources */, + 1D60588D0D05DD3D006BFB54 /* Resources */, + 1D60588E0D05DD3D006BFB54 /* Sources */, + 1D60588F0D05DD3D006BFB54 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 301BF551109A68C00062928A /* PBXTargetDependency */, + ); + name = "MovieVerse"; + productName = "MovieVerse"; + productReference = 1D6058910D05DD3D006BFB54 /* MovieVerse.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1130; + TargetAttributes = { + 1D6058900D05DD3D006BFB54 = { + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "MovieVerse" */; + compatibilityVersion = "Xcode 11.0"; + developmentRegion = en; + hasScannedForEncodings = 1; + knownRegions = ( + en, + Base, + ); + mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 301BF52E109A57CC0062928A /* Products */; + ProjectRef = 301BF52D109A57CC0062928A /* CordovaLib/CordovaLib.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 1D6058900D05DD3D006BFB54 /* MovieVerse */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 301BF535109A57CC0062928A /* libCordova.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libCordova.a; + remoteRef = 301BF534109A57CC0062928A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 907D8124214C687600058A10 /* Cordova.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Cordova.framework; + remoteRef = 907D8123214C687600058A10 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 1D60588D0D05DD3D006BFB54 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 302D95F214D2391D003F00A1 /* MainViewController.xib in Resources */, + 0207DA581B56EA530066E2B4 /* Assets.xcassets in Resources */, + 6AFF5BF91D6E424B00AB3073 /* CDVLaunchScreen.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D60588E0D05DD3D006BFB54 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D60589B0D05DD56006BFB54 /* main.m in Sources */, + 1D3623260D0F684500981E51 /* AppDelegate.m in Sources */, + 302D95F114D2391D003F00A1 /* MainViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 301BF551109A68C00062928A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = CordovaLib; + targetProxy = 301BF550109A68C00062928A /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 1D6058940D05DD3E006BFB54 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3047A50F1AB8059700498E2A /* build-debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "MovieVerse/MovieVerse-Prefix.pch"; + GCC_THUMB_SUPPORT = NO; + GCC_VERSION = ""; + INFOPLIST_FILE = "MovieVerse/MovieVerse-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = io.cordova.hellocordova; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTS_MACCATALYST = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_WORKSPACE = NO; + }; + name = Debug; + }; + 1D6058950D05DD3E006BFB54 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3047A5101AB8059700498E2A /* build-release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + COPY_PHASE_STRIP = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "MovieVerse/MovieVerse-Prefix.pch"; + GCC_THUMB_SUPPORT = NO; + GCC_VERSION = ""; + INFOPLIST_FILE = "MovieVerse/MovieVerse-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = io.cordova.hellocordova; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTS_MACCATALYST = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_WORKSPACE = NO; + }; + name = Release; + }; + C01FCF4F08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3047A5111AB8059700498E2A /* build.xcconfig */; + buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_THUMB_SUPPORT = NO; + GCC_VERSION = ""; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MERGED_BINARY_TYPE = automatic; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SKIP_INSTALL = NO; + WK_WEB_VIEW_ONLY = 1; + }; + name = Debug; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3047A5111AB8059700498E2A /* build.xcconfig */; + buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_THUMB_SUPPORT = NO; + GCC_VERSION = ""; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MERGED_BINARY_TYPE = automatic; + SDKROOT = iphoneos; + SKIP_INSTALL = NO; + WK_WEB_VIEW_ONLY = 1; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "MovieVerse" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D6058940D05DD3E006BFB54 /* Debug */, + 1D6058950D05DD3E006BFB54 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "MovieVerse" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/MovieVerse-Mobile/platforms/ios/MovieVerse.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/MovieVerse-Mobile/platforms/ios/MovieVerse.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/MovieVerse.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/MovieVerse-Mobile/platforms/ios/MovieVerse.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/MovieVerse-Mobile/platforms/ios/MovieVerse.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/MovieVerse.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/MovieVerse-Mobile/platforms/ios/MovieVerse.xcworkspace/contents.xcworkspacedata b/MovieVerse-Mobile/platforms/ios/MovieVerse.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..9f6974c2 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/MovieVerse.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/MovieVerse-Mobile/platforms/ios/MovieVerse.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/MovieVerse-Mobile/platforms/ios/MovieVerse.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/MovieVerse.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/MovieVerse-Mobile/platforms/ios/MovieVerse.xcworkspace/xcshareddata/xcschemes/MovieVerse.xcscheme b/MovieVerse-Mobile/platforms/ios/MovieVerse.xcworkspace/xcshareddata/xcschemes/MovieVerse.xcscheme new file mode 100644 index 00000000..cf5627f4 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/MovieVerse.xcworkspace/xcshareddata/xcschemes/MovieVerse.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MovieVerse-Mobile/platforms/ios/MovieVerse/AppDelegate.h b/MovieVerse-Mobile/platforms/ios/MovieVerse/AppDelegate.h new file mode 100644 index 00000000..8c27551c --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/MovieVerse/AppDelegate.h @@ -0,0 +1,24 @@ +/* + 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. + */ + +#import + +@interface AppDelegate : CDVAppDelegate + +@end diff --git a/MovieVerse-Mobile/platforms/ios/MovieVerse/AppDelegate.m b/MovieVerse-Mobile/platforms/ios/MovieVerse/AppDelegate.m new file mode 100644 index 00000000..3d2b6ffd --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/MovieVerse/AppDelegate.m @@ -0,0 +1,31 @@ +/* + 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. + */ + +#import "AppDelegate.h" +#import "MainViewController.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + self.viewController = [[MainViewController alloc] init]; + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/MovieVerse-Mobile/platforms/ios/MovieVerse/Bridging-Header.h b/MovieVerse-Mobile/platforms/ios/MovieVerse/Bridging-Header.h new file mode 100644 index 00000000..5d8abc98 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/MovieVerse/Bridging-Header.h @@ -0,0 +1,28 @@ +/* + 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. + */ +// +// Bridging-Header.h +// __PROJECT_NAME__ +// +// Created by ___FULLUSERNAME___ on ___DATE___. +// Copyright ___ORGANIZATIONNAME___ ___YEAR___. All rights reserved. +// +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +#import diff --git a/MovieVerse-Mobile/platforms/ios/MovieVerse/CDVLaunchScreen.storyboard b/MovieVerse-Mobile/platforms/ios/MovieVerse/CDVLaunchScreen.storyboard new file mode 100644 index 00000000..924392e3 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/MovieVerse/CDVLaunchScreen.storyboard @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MovieVerse-Mobile/platforms/ios/MovieVerse/Entitlements-Debug.plist b/MovieVerse-Mobile/platforms/ios/MovieVerse/Entitlements-Debug.plist new file mode 100644 index 00000000..1ed4ae5b --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/MovieVerse/Entitlements-Debug.plist @@ -0,0 +1,24 @@ + + + + + + + diff --git a/MovieVerse-Mobile/platforms/ios/MovieVerse/Entitlements-Release.plist b/MovieVerse-Mobile/platforms/ios/MovieVerse/Entitlements-Release.plist new file mode 100644 index 00000000..1ed4ae5b --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/MovieVerse/Entitlements-Release.plist @@ -0,0 +1,24 @@ + + + + + + + diff --git a/MovieVerse-Mobile/platforms/ios/MovieVerse/MainViewController.h b/MovieVerse-Mobile/platforms/ios/MovieVerse/MainViewController.h new file mode 100644 index 00000000..1bbe01c2 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/MovieVerse/MainViewController.h @@ -0,0 +1,24 @@ +/* + 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. + */ + +#import + +@interface MainViewController : CDVViewController + +@end diff --git a/MovieVerse-Mobile/platforms/ios/MovieVerse/MainViewController.m b/MovieVerse-Mobile/platforms/ios/MovieVerse/MainViewController.m new file mode 100644 index 00000000..b99433a0 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/MovieVerse/MainViewController.m @@ -0,0 +1,30 @@ +/* + 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. + */ + +#import "MainViewController.h" + +@implementation MainViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + [self.launchView setAlpha:1]; +} + +@end diff --git a/MovieVerse-Mobile/platforms/ios/MovieVerse/MainViewController.xib b/MovieVerse-Mobile/platforms/ios/MovieVerse/MainViewController.xib new file mode 100644 index 00000000..e45d65c6 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/MovieVerse/MainViewController.xib @@ -0,0 +1,138 @@ + + + + + 1280 + 11C25 + 1919 + 1138.11 + 566.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 916 + + + IBProxyObject + IBUIView + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + + 274 + {{0, 20}, {320, 460}} + + + + 3 + MQA + + 2 + + + + IBCocoaTouchFramework + + + + + + + view + + + + 3 + + + + + + 0 + + + + + + 1 + + + + + -1 + + + File's Owner + + + -2 + + + + + + + MainViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + 3 + + + + + MainViewController + UIViewController + + IBProjectSource + ./Classes/MainViewController.h + + + + + 0 + IBCocoaTouchFramework + YES + 3 + 916 + + diff --git a/MovieVerse-Mobile/platforms/ios/MovieVerse/MovieVerse-Info.plist b/MovieVerse-Mobile/platforms/ios/MovieVerse/MovieVerse-Info.plist new file mode 100644 index 00000000..1e42ca28 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/MovieVerse/MovieVerse-Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + en_US + CFBundleDisplayName + MovieVerse + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0.0 + LSRequiresIPhoneOS + + NSMainNibFile + + NSMainNibFile~ipad + + UILaunchStoryboardName + CDVLaunchScreen + UIRequiresFullScreen + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeRight + + + \ No newline at end of file diff --git a/MovieVerse-Mobile/platforms/ios/MovieVerse/MovieVerse-Prefix.pch b/MovieVerse-Mobile/platforms/ios/MovieVerse/MovieVerse-Prefix.pch new file mode 100644 index 00000000..75b462b3 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/MovieVerse/MovieVerse-Prefix.pch @@ -0,0 +1,26 @@ +/* + 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. + */ +// +// Prefix header for all source files of the 'MovieVerse' target in the 'MovieVerse' project +// + +#ifdef __OBJC__ + #import + #import +#endif diff --git a/MovieVerse-Mobile/platforms/ios/cordova/build-debug.xcconfig b/MovieVerse-Mobile/platforms/ios/cordova/build-debug.xcconfig new file mode 100644 index 00000000..7e7985bb --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/cordova/build-debug.xcconfig @@ -0,0 +1,32 @@ +// +// 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. +// + +// +// XCode Build settings for "Debug" Build Configuration. +// + +#include "build.xcconfig" + +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) DEBUG=1 + +#include "build-extras.xcconfig" + +// (CB-11792) +// @COCOAPODS_SILENCE_WARNINGS@ // +#include "../pods-debug.xcconfig" diff --git a/MovieVerse-Mobile/platforms/ios/cordova/build-extras.xcconfig b/MovieVerse-Mobile/platforms/ios/cordova/build-extras.xcconfig new file mode 100644 index 00000000..7e631112 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/cordova/build-extras.xcconfig @@ -0,0 +1,22 @@ +// +// 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. +// + +// +// Auto-generated config file to override configuration files (build-release/build-debug). +// diff --git a/MovieVerse-Mobile/platforms/ios/cordova/build-release.xcconfig b/MovieVerse-Mobile/platforms/ios/cordova/build-release.xcconfig new file mode 100644 index 00000000..70b0f073 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/cordova/build-release.xcconfig @@ -0,0 +1,33 @@ +// +// 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. +// + +// +// XCode Build settings for "Release" Build Configuration. +// + +#include "build.xcconfig" + +CODE_SIGN_IDENTITY = iPhone Distribution +CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Distribution + +#include "build-extras.xcconfig" + +// (CB-11792) +// @COCOAPODS_SILENCE_WARNINGS@ // +#include "../pods-release.xcconfig" diff --git a/MovieVerse-Mobile/platforms/ios/pods-debug.xcconfig b/MovieVerse-Mobile/platforms/ios/pods-debug.xcconfig new file mode 100644 index 00000000..12c70651 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/pods-debug.xcconfig @@ -0,0 +1,20 @@ +// +// 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. +// + +// DO NOT MODIFY -- auto-generated by Apache Cordova diff --git a/MovieVerse-Mobile/platforms/ios/pods-release.xcconfig b/MovieVerse-Mobile/platforms/ios/pods-release.xcconfig new file mode 100644 index 00000000..12e2b13f --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/pods-release.xcconfig @@ -0,0 +1,20 @@ +// +// 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. +// + +// DO NOT MODIFY -- auto-generated by Apache Cordova \ No newline at end of file diff --git a/MovieVerse-Mobile/platforms/ios/www/src/css/chatbot.css b/MovieVerse-Mobile/platforms/ios/www/src/css/chatbot.css new file mode 100644 index 00000000..d8386574 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/www/src/css/chatbot.css @@ -0,0 +1,716 @@ +@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;400;600&display=swap'); + +html { + height: 100%; + overflow-x: hidden; + scroll-behavior: smooth; +} + +body { + color: #ff8623; + font-family: "Poppins", sans-serif; + margin: 0; + align-content: center; + text-align: center; + overflow: auto; + height: 100%; + background-image: url("https://wallpaperaccess.com/full/433561.jpg"); + background-size: cover; + background-position: center; + background-repeat: no-repeat; + background-attachment: fixed; + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + min-height: 100vh; + overflow-x: hidden; +} + +#form { + margin-top: -14px; +} + +.overview { + position: absolute; + padding: 2rem; + top: 0; + left: 0; + bottom: 0; + right: 0; + background-color: #fff; + color: black; + transform: translateY(100%); + transition: opacity 0.3s ease-in, visibility 0.3s ease-in, transform 0.3s ease-in; + overflow-y: auto; + visibility: hidden; +} + +.movie:hover .overview { + transform: translateY(0); + opacity: 1; + visibility: visible; +} + +.overview h4 { + margin-top: 0; +} + +.movie-info span.green { + color: rgb(39,189,39); +} + +.movie-info span.orange { + color: orange; +} + +.movie-info span.red { + color: rgb(189,42,42); +} + +#my-heading { + color: #ff8623; + padding: 10px; + font-size: 36px; + text-align: center; + background-color: transparent; + margin-top: 15px; + left: 50%; + margin-left: 41px; +} + +.movie { + background-color: #373b69; + border-radius: 3px; + box-shadow: 0 4px 5px rgba(0,0,0,0.2); + margin: 1rem; + color: white; + position: relative; + overflow: hidden; + width: 300px; +} + +.movie img { + width: 100%; +} + +.movie-info { + color: #eee; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.5rem 1rem 1rem; + letter-spacing: 0.5px; +} + +.movie-info h3 { + margin: 0; +} + +.movie:hover .overview { + transform: translateY(0); +} + +.movie-info span { + background-color: #22254b; + border-radius: 3px; + font-weight: bold; + padding: 0.25rem 0.5rem; +} + +header { + display: flex; + align-items: center; + justify-content: space-between; + padding-left: 0.5vw; + padding-right: 3vw; + padding-top: -20px; + width: 96vw; +} + +.highlight { + color: orange; +} + +header h1 { + color: #ff8623; + text-align: center; +} + +#movie-timeline-btn { + position: fixed; + bottom: 180px; + right: 20px; + background-color: #7378c5; + font: inherit; + color: black; + border-radius: 8px; + border: none; + cursor: pointer; + z-index: 1000; +} + +#movie-timeline-btn:hover { + background-color: #ff8623; + transition: 0.3s ease-in; +} + +#movie-of-the-day-btn { + position: fixed; + bottom: 140px; + right: 20px; + background-color: #7378c5; + font: inherit; + color: black; + border-radius: 8px; + border: none; + cursor: pointer; + z-index: 1000; +} + +#trailerButton { + background-color: #7378c5; + font: inherit; + color: black; + border-radius: 8px; + border: none; + cursor: pointer; + z-index: 1000; +} + +#trailerButton:hover { + background-color: #ff8623; + transition: 0.3s ease-in; +} + +#movie-of-the-day-btn:hover { + background-color: #ff8623; + transition: 0.1s linear; +} + +#my-heading { + color: #ff8623; + padding: 10px; + font-size: 36px; + text-align: center; + background-color: transparent; +} + +#back-to-top-btn { + position: fixed; + bottom: 20px; + right: 20px; + background-color: #7378c5; + font: inherit; + color: black; + border-radius: 8px; + border: none; +} + +#back-to-top-btn:hover { + background-color: #ff8623; + transition: 0.1s linear; +} + +#discussion-input { + background-color: transparent; + border: 2px solid #ff8623; + border-radius: 50px; + color: #fff; + font-family: inherit; + font-size: 1rem; + padding: 0.5rem 1rem; +} + +#discussion-input:hover { + background-color: #ff8623; + transition: 0.1s linear; +} + +#post-btn { + background-color: #ff8623; + font: inherit; +} + +#post-btn:hover { + background-color: #7f4caf; + transition: 0.1s linear; +} + +ol li:hover { + color: #7f4caf; + transition: 0.1s linear; +} + +#chatbotContainer { + width: 80%; + max-width: 600px; + background-color: rgba(0, 0, 0, 0.7); + margin: 20px auto; + border-radius: 8px; + padding: 10px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +#chatbotHeader { + background-color: #ff8623; + color: white; + padding: 10px; + text-align: center; + border-radius: 8px; + margin-top: 10px; +} + +#chatbotBody { + overflow-y: auto; + max-height: 300px; + padding: 10px; + border-radius: 8px; + color: white; + word-wrap: break-word; + box-sizing: border-box; + margin-top: 10px; + border: 1px solid #ff8623; +} + +#sendButton { + padding: 10px; + margin-top: 10px; + border-radius: 8px; + font-family: inherit; + font-size: 1rem; + color: white; + background-color: #ff8623; + border: none; + cursor: pointer; + display: block; + margin-left: auto; + margin-right: auto; +} + +#sendButton:hover { + background-color: #7f4caf; + transition: 0.1s linear; +} + +#chatbotInput { + width: 100%; + padding: 10px 20px; + border: 1px solid #ff8623; + border-radius: 8px; + font-family: inherit; + font-size: 1rem; + background-color: transparent; + color: white; + margin-top: 10px; + box-sizing: border-box; +} + +.highlight1 { + color: orange; +} + +#chatbotInput:focus { + outline: none; +} + +#chatbotBody > div { + margin-bottom: 10px; + padding: 5px 10px; + border-radius: 5px; +} + +#chatbotBody > div[text-align="right"] { + background-color: #ff8623; + align-self: flex-end; +} + +#chatbotBody > div[text-align="left"] { + background-color: #7378c5; + align-self: flex-start; +} + +.favorites-btn { + background-color: #7378c5; + border: none; + color: white; + height: 40px; + text-align: center; + text-decoration: none; + display: inline-block; + font: inherit; + font-size: 16px; + cursor: pointer; + position: absolute; + top: 0; + left: 288px; + transform: translateX(-50%); + border-radius: 8px; + padding-left: 10px; + padding-right: 10px; + box-sizing: border-box; +} + +.favorites-btn:hover { + background-color: #ff8623; + transition: 0.3s ease-in; +} + +#profileBtn { + background-color: #7378c5; + border: none; + color: white; + height: 40px; + text-align: center; + text-decoration: none; + display: inline-block; + font: inherit; + font-size: 16px; + cursor: pointer; + position: absolute; + width: 100px; + top: 0; + left: 338px; + border-radius: 8px; +} + +#profileBtn:hover { + background-color: #ff8623; + transition: 0.3s ease-in; +} + +.about { + background-color: #7378c5; + border: none; + color: white; + width: 85px; + height: 40px; + text-align: center; + text-decoration: none; + display: inline-block; + font: inherit; + font-size: 16px; + cursor: pointer; + position: absolute; + top: 0; + left: 196px; + transform: translateX(-50%); + border-radius: 8px; +} + +#googleSignInBtn { + background-color: #7378c5; + border: none; + color: white; + height: 40px; + text-align: center; + font: inherit; + font-size: 16px; + cursor: pointer; + position: absolute; + top: 0; + left: 441px; + border-radius: 8px; + padding: 0 10px; + box-sizing: border-box; +} + +#googleSignInBtn:hover { + background-color: #ff8623; + transition: 0.3s ease-in; +} + +.about:hover { + background-color: #ff8623; + transition: 0.3s ease-in; +} + +.back-btn { + background-color: #7378c5; + border: none; + color: white; + width: 85px; + height: 40px; + text-align: center; + text-decoration: none; + display: inline-block; + font: inherit; + font-size: 16px; + cursor: pointer; + position: absolute; + top: 0; + left: 107px; + transform: translateX(-50%); + border-radius: 8px; +} + +.back-btn:hover { + background-color: #ff8623; + transition: 0.3s ease-in; +} + +#discussions-btn { + position: fixed; + bottom: 140px; + right: 20px; + background-color: #7378c5; + font: inherit; + color: black; + border-radius: 8px; + border: none; + cursor: pointer; +} + +#discussions-btn:hover { + background-color: #ff8623; + transition: 0.1s linear; +} + +#trivia-btn { + position: fixed; + bottom: 100px; + right: 20px; + background-color: #7378c5; + font: inherit; + color: black; + border-radius: 8px; + border: none; + cursor: pointer; + z-index: 1000; +} + +#settings-btn { + background-color: #7378c5; + bottom: 260px; + right: 20px; + color: black; + border-radius: 8px; + border: none; + cursor: pointer; + font: inherit; +} + +#alt-home-btn { + position: fixed; + bottom: 185px; + right: 20px; + background-color: #7378c5; + font: inherit; + color: black; + border-radius: 8px; + border: none; + cursor: pointer; + z-index: 1000; + display: none; +} + +#alt-home-btn:hover { + background-color: #ff8623; + transition: 0.1s linear; +} + +#settings-btn:hover { + background-color: #ff8623; + transition: 0.1s ease-in; +} + +#trivia-btn:hover { + background-color: #ff8623; + transition: 0.1s linear; +} + +#buttons-container { + position: fixed; + top: 20px; + right: -30px; +} + +@media (max-width: 906px) { + #local-time, + #googleSignInBtn, + #profileBtn { + display: none; + } +} + +#local-time { + margin-top: 30px; + margin-left: 60px; + align-items: center; +} + +.clock:hover { + color: #f509d9; +} + +#time-label { + color: #ff8623; +} + +#time-label:hover { + color: #f509d9; +} + +.search { + background-color: transparent; + border: 2px solid #121280; + border-radius: 50px; + color: #fff; + font-family: inherit; + font-size: 1rem; + padding: 0.5rem 1rem; + width: 250px; +} + +main { + width: 100%; + display: flex; + justify-content: center; + align-items: stretch; + flex-wrap: wrap; + margin: 0 auto; + padding: 20px; +} + +#bot-header { + text-align: center; + width: 100%; +} + +.search::placeholder { + color: #7378c5; +} + +#search:hover { + outline: none; + background-color: #22254b; +} + +.search:focus { + outline: none; + background-color: #22254b; +} + +#button-search { + color: #171616; + background-color: #7378c5; + font: inherit; + border-radius: 8px; + border: none; + cursor: pointer; +} + +@media (max-width: 780px) { + #my-heading { + margin-top: 20px; + } + .search { + margin-top: 10px; + } + #chatbotContainer { + top: 55%; + } +} + +@media (max-height: 680px) { + header { + display: none; + } + #alt-home-btn { + display: block; + } +} + +#button-search:hover { + background-color: #ff8623; + transition: 0.1s linear; +} + +#clear-search-btn { + background-color: #7378c5; + color: black; + border: none; + padding: 5px 10px; + border-radius: 5px; + cursor: pointer; + margin-left: 10px; + font: inherit; +} + +#clear-search-btn:hover { + background-color: #ff8623; +} + +@media (max-width: 906px) { + .back-btn, .about, .favorites-btn { + position: absolute; + top: 0; + left: 50%; + transform: translateX(-50%); + } + + .back-btn { + margin-left: -90px; + } + + .about { + margin-left: 0px; + } + + .favorites-btn { + margin-left: 93px; + } + + header { + padding-bottom: 20px; + justify-content: space-between; + padding: 1.5rem; + display: flex; + flex-direction: column; + align-items: center; + } + + main { + margin-bottom: -40px; + justify-content: space-between; + padding: 1.5rem; + display: flex; + flex-direction: column; + align-items: center; + } + + #my-heading { + margin-left: 0; + } +} + +@media (max-width: 705px) { + #search-title, + #alt-title { + display: none; + } +} + +@media (max-height: 930px) { + #search-title, + #alt-title { + display: none; + } +} + +#movie-match-btn2 { + position: fixed; + bottom: 140px; + right: 20px; + background-color: #7378c5; + font: inherit; + color: black; + border-radius: 8px; + border: none; + cursor: pointer; + z-index: 1000; +} diff --git a/MovieVerse-Mobile/platforms/ios/www/src/js/about.js b/MovieVerse-Mobile/platforms/ios/www/src/js/about.js new file mode 100644 index 00000000..45b1b39d --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/www/src/js/about.js @@ -0,0 +1,280 @@ +async function showMovieOfTheDay() { + const year = new Date().getFullYear(); + const url = `https://api.themoviedb.org/3/discover/movie?api_key=c5a20c861acf7bb8d9e987dcc7f1b558&sort_by=vote_average.desc&vote_count.gte=100&primary_release_year=${year}&vote_average.gte=7`; + + try { + const response = await fetch(url); + const data = await response.json(); + const movies = data.results; + + if (movies.length > 0) { + const randomMovie = movies[Math.floor(Math.random() * movies.length)]; + localStorage.setItem('selectedMovieId', randomMovie.id); + window.location.href = 'movie-details.html'; + } + else { + fallbackMovieSelection(); + } + } + catch (error) { + console.error('Error fetching movie:', error); + fallbackMovieSelection(); + } +} + +function fallbackMovieSelection() { + const fallbackMovies = [432413, 299534, 1726, 562, 118340, 455207, 493922, 447332, 22970, 530385, 27205, 264660, 120467, 603, 577922, 76341, 539, 419704, 515001, 118340, 424, 98]; + const randomFallbackMovie = fallbackMovies[Math.floor(Math.random() * fallbackMovies.length)]; + localStorage.setItem('selectedMovieId', randomFallbackMovie); + window.location.href = 'movie-details.html'; +} + +function handleSignInOut() { + const isSignedIn = JSON.parse(localStorage.getItem('isSignedIn')) || false; + + if (isSignedIn) { + localStorage.setItem('isSignedIn', JSON.stringify(false)); + alert('You have been signed out.'); + } + else { + window.location.href = 'sign-in.html'; + return; + } + + updateSignInButtonState(); +} + +function updateSignInButtonState() { + const isSignedIn = JSON.parse(localStorage.getItem('isSignedIn')) || false; + + const signInText = document.getElementById('signInOutText'); + const signInIcon = document.getElementById('signInIcon'); + const signOutIcon = document.getElementById('signOutIcon'); + + if (isSignedIn) { + signInText.textContent = 'Sign Out'; + signInIcon.style.display = 'none'; + signOutIcon.style.display = 'inline-block'; + } + else { + signInText.textContent = 'Sign In'; + signInIcon.style.display = 'inline-block'; + signOutIcon.style.display = 'none'; + } +} + +document.addEventListener("DOMContentLoaded", function() { + updateSignInButtonState(); + document.getElementById('googleSignInBtn').addEventListener('click', handleSignInOut); +}); + +document.getElementById('settings-btn').addEventListener('click', () => { + window.location.href = 'settings.html'; +}); + +document.addEventListener('DOMContentLoaded', () => { + applySettings(); + + function applySettings() { + const savedBg = localStorage.getItem('backgroundImage'); + const savedTextColor = localStorage.getItem('textColor'); + const savedFontSize = localStorage.getItem('fontSize'); + + if (savedBg) { + document.body.style.backgroundImage = `url('${savedBg}')`; + } + if (savedTextColor) { + document.querySelectorAll('h1, h2, h3, p, a, span, div, button, input, select, textarea, label, li').forEach(element => { + element.style.color = savedTextColor; + }); + } + if (savedFontSize) { + const size = savedFontSize === 'small' ? '12px' : savedFontSize === 'medium' ? '16px' : '20px'; + document.body.style.fontSize = size; + } + } +}); + +/** + * Rotates the user stats and movie quotes displayed in the main element. + */ +function rotateUserStats() { + const stats = [ + { + label: "Your Current Time", + getValue: () => { + const now = new Date(); + let hours = now.getHours(); + let minutes = now.getMinutes(); + hours = hours < 10 ? '0' + hours : hours; + minutes = minutes < 10 ? '0' + minutes : minutes; + return `${hours}:${minutes}`; + } + }, + { label: "Most Visited Movie", getValue: getMostVisitedMovie }, + { label: "Your Most Visited Director", getValue: getMostVisitedDirector }, + { label: "Your Most Visited Actor", getValue: getMostVisitedActor }, + { + label: "Your Unique Movies Discovered", + getValue: () => { + const viewedMovies = JSON.parse(localStorage.getItem('uniqueMoviesViewed')) || []; + return viewedMovies.length; + } + }, + { + label: "Your Favorited Movies", + getValue: () => { + const favoritedMovies = JSON.parse(localStorage.getItem('favorites')) || []; + return favoritedMovies.length; + } + }, + { + label: "Your Most Common Favorited Genre", + getValue: getMostCommonGenre + }, + { label: "Your Created Watchlists", getValue: () => localStorage.getItem('watchlistsCreated') || 0 }, + { label: "Your Average Movie Rating", getValue: () => localStorage.getItem('averageMovieRating') || 'Not Rated' }, + { + label: "Your Unique Directors Discovered", + getValue: () => { + const viewedDirectors = JSON.parse(localStorage.getItem('uniqueDirectorsViewed')) || []; + return viewedDirectors.length; + } + }, + { + label: "Your Unique Actors Discovered", + getValue: () => { + const viewedActors = JSON.parse(localStorage.getItem('uniqueActorsViewed')) || []; + return viewedActors.length; + } + }, + { + label: "Your Unique Production Companies Discovered", + getValue: () => { + const viewedCompanies = JSON.parse(localStorage.getItem('uniqueCompaniesViewed')) || []; + return viewedCompanies.length; + } + }, + { label: "Your Trivia Accuracy", getValue: getTriviaAccuracy }, + { + label: "Quote from Forrest Gump (1994)", + getValue: () => "Life was like a box of chocolates. You never know what you're gonna get." + }, + { + label: "Quote from The Lord of the Rings: The Two Towers (2002)", + getValue: () => "There is some good in this world, and it's worth fighting for." + }, + { + label: "Quote from Fight Club (1999)", + getValue: () => "The first rule of Fight Club is: You do not talk about Fight Club." + }, + { + label: "Quote from The Wizard of Oz (1939)", + getValue: () => "There's no place like home." + }, + { + label: "Quote from Cool Hand Luke (1967)", + getValue: () => "What we've got here is failure to communicate." + }, + ]; + + let currentStatIndex = 0; + + function updateStatDisplay() { + const currentStat = stats[currentStatIndex]; + document.getElementById('stats-label').textContent = currentStat.label + ':'; + document.getElementById('stats-display').textContent = currentStat.getValue(); + currentStatIndex = (currentStatIndex + 1) % stats.length; + } + + updateStatDisplay(); + + const localTimeDiv = document.getElementById('local-time'); + let statRotationInterval = setInterval(updateStatDisplay, 3000); + + localTimeDiv.addEventListener('click', () => { + clearInterval(statRotationInterval); + updateStatDisplay(); + statRotationInterval = setInterval(updateStatDisplay, 3000); + }); +} + +function updateMovieVisitCount(movieId, movieTitle) { + let movieVisits = JSON.parse(localStorage.getItem('movieVisits')) || {}; + if (!movieVisits[movieId]) { + movieVisits[movieId] = { count: 0, title: movieTitle }; + } + movieVisits[movieId].count += 1; + localStorage.setItem('movieVisits', JSON.stringify(movieVisits)); +} + +function getMostVisitedMovie() { + const movieVisits = JSON.parse(localStorage.getItem('movieVisits')) || {}; + let mostVisitedMovie = ''; + let maxVisits = 0; + + for (const movieId in movieVisits) { + if (movieVisits[movieId].count > maxVisits) { + mostVisitedMovie = movieVisits[movieId].title; + maxVisits = movieVisits[movieId].count; + } + } + + return mostVisitedMovie || 'Not Available'; +} + +function getMostVisitedActor() { + const actorVisits = JSON.parse(localStorage.getItem('actorVisits')) || {}; + let mostVisitedActor = ''; + let maxVisits = 0; + + for (const actorId in actorVisits) { + if (actorVisits[actorId].count > maxVisits) { + mostVisitedActor = actorVisits[actorId].name; + maxVisits = actorVisits[actorId].count; + } + } + + return mostVisitedActor || 'Not Available'; +} + +function getMostVisitedDirector() { + const directorVisits = JSON.parse(localStorage.getItem('directorVisits')) || {}; + let mostVisitedDirector = ''; + let maxVisits = 0; + + for (const directorId in directorVisits) { + if (directorVisits[directorId].count > maxVisits) { + mostVisitedDirector = directorVisits[directorId].name; + maxVisits = directorVisits[directorId].count; + } + } + + return mostVisitedDirector || 'Not Available'; +} + +function getTriviaAccuracy() { + let triviaStats = JSON.parse(localStorage.getItem('triviaStats')) || { totalCorrect: 0, totalAttempted: 0 }; + if (triviaStats.totalAttempted === 0) { + return 'No trivia attempted'; + } + let accuracy = (triviaStats.totalCorrect / triviaStats.totalAttempted) * 100; + return `${accuracy.toFixed(1)}% accuracy`; +} + +function getMostCommonGenre() { + const favoriteGenres = JSON.parse(localStorage.getItem('favoriteGenres')) || {}; + let mostCommonGenre = ''; + let maxCount = 0; + + for (const genre in favoriteGenres) { + if (favoriteGenres[genre] > maxCount) { + mostCommonGenre = genre; + maxCount = favoriteGenres[genre]; + } + } + + return mostCommonGenre || 'Not Available'; +} + +document.addEventListener('DOMContentLoaded', rotateUserStats); \ No newline at end of file diff --git a/MovieVerse-Mobile/platforms/ios/www/src/js/favorites.js b/MovieVerse-Mobile/platforms/ios/www/src/js/favorites.js new file mode 100644 index 00000000..2edee621 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/www/src/js/favorites.js @@ -0,0 +1,1007 @@ +document.addEventListener("DOMContentLoaded", function() { + checkAndReloadPageIfNeeded(); + loadWatchLists(); + updateSignInButton(); + initClient(); + updateFavorites(); +}); + +function checkAndReloadPageIfNeeded() { + const hasReloaded = localStorage.getItem('hasReloaded'); + + if (!hasReloaded) { + localStorage.setItem('hasReloaded', true); + window.location.reload(); + } +} + +function updateFavorites() { + const favorites = JSON.parse(localStorage.getItem('favorites')) || []; + const favoritesMain = document.getElementById('favorites-main'); + favoritesMain.style.textAlign = 'center'; + if (favorites.length > 0) { + favorites.forEach(movieId => fetchMovieDetails(movieId)); + } + else { + favoritesMain.innerHTML = '
\n' + + '

No Favorite Movies Added Yet.

\n' + + '
`;'; + } +} + +function rotateUserStats() { + const stats = [ + { + label: "Your Current Time", + getValue: () => { + const now = new Date(); + let hours = now.getHours(); + let minutes = now.getMinutes(); + hours = hours < 10 ? '0' + hours : hours; + minutes = minutes < 10 ? '0' + minutes : minutes; + return `${hours}:${minutes}`; + } + }, + { label: "Most Visited Movie", getValue: getMostVisitedMovie }, + { label: "Your Most Visited Director", getValue: getMostVisitedDirector }, + { label: "Your Most Visited Actor", getValue: getMostVisitedActor }, + { + label: "Your Unique Movies Discovered", + getValue: () => { + const viewedMovies = JSON.parse(localStorage.getItem('uniqueMoviesViewed')) || []; + return viewedMovies.length; + } + }, + { + label: "Your Favorited Movies", + getValue: () => { + const favoritedMovies = JSON.parse(localStorage.getItem('favorites')) || []; + return favoritedMovies.length; + } + }, + { + label: "Your Most Common Favorited Genre", + getValue: getMostCommonGenre + }, + { label: "Your Created Watchlists", getValue: () => localStorage.getItem('watchlistsCreated') || 0 }, + { label: "Your Average Movie Rating", getValue: () => localStorage.getItem('averageMovieRating') || 'Not Rated' }, + { + label: "Your Unique Directors Discovered", + getValue: () => { + const viewedDirectors = JSON.parse(localStorage.getItem('uniqueDirectorsViewed')) || []; + return viewedDirectors.length; + } + }, + { + label: "Your Unique Actors Discovered", + getValue: () => { + const viewedActors = JSON.parse(localStorage.getItem('uniqueActorsViewed')) || []; + return viewedActors.length; + } + }, + { + label: "Your Unique Production Companies Discovered", + getValue: () => { + const viewedCompanies = JSON.parse(localStorage.getItem('uniqueCompaniesViewed')) || []; + return viewedCompanies.length; + } + }, + { label: "Your Trivia Accuracy", getValue: getTriviaAccuracy }, + { + label: "Quote from Forrest Gump (1994)", + getValue: () => "Life was like a box of chocolates. You never know what you're gonna get." + }, + { + label: "Quote from The Lord of the Rings: The Two Towers (2002)", + getValue: () => "There is some good in this world, and it's worth fighting for." + }, + { + label: "Quote from Fight Club (1999)", + getValue: () => "The first rule of Fight Club is: You do not talk about Fight Club." + }, + { + label: "Quote from The Wizard of Oz (1939)", + getValue: () => "There's no place like home." + }, + { + label: "Quote from Cool Hand Luke (1967)", + getValue: () => "What we've got here is failure to communicate." + }, + ]; + + let currentStatIndex = 0; + + function updateStatDisplay() { + const currentStat = stats[currentStatIndex]; + document.getElementById('stats-label').textContent = currentStat.label + ':'; + document.getElementById('stats-display').textContent = currentStat.getValue(); + currentStatIndex = (currentStatIndex + 1) % stats.length; + } + + updateStatDisplay(); + + const localTimeDiv = document.getElementById('local-time'); + let statRotationInterval = setInterval(updateStatDisplay, 3000); + + localTimeDiv.addEventListener('click', () => { + clearInterval(statRotationInterval); + updateStatDisplay(); + statRotationInterval = setInterval(updateStatDisplay, 3000); + }); +} + +function updateMovieVisitCount(movieId, movieTitle) { + let movieVisits = JSON.parse(localStorage.getItem('movieVisits')) || {}; + if (!movieVisits[movieId]) { + movieVisits[movieId] = { count: 0, title: movieTitle }; + } + movieVisits[movieId].count += 1; + localStorage.setItem('movieVisits', JSON.stringify(movieVisits)); +} + +function getMostVisitedMovie() { + const movieVisits = JSON.parse(localStorage.getItem('movieVisits')) || {}; + let mostVisitedMovie = ''; + let maxVisits = 0; + + for (const movieId in movieVisits) { + if (movieVisits[movieId].count > maxVisits) { + mostVisitedMovie = movieVisits[movieId].title; + maxVisits = movieVisits[movieId].count; + } + } + + return mostVisitedMovie || 'Not Available'; +} + +function getMostVisitedActor() { + const actorVisits = JSON.parse(localStorage.getItem('actorVisits')) || {}; + let mostVisitedActor = ''; + let maxVisits = 0; + + for (const actorId in actorVisits) { + if (actorVisits[actorId].count > maxVisits) { + mostVisitedActor = actorVisits[actorId].name; + maxVisits = actorVisits[actorId].count; + } + } + + return mostVisitedActor || 'Not Available'; +} + +function getMostVisitedDirector() { + const directorVisits = JSON.parse(localStorage.getItem('directorVisits')) || {}; + let mostVisitedDirector = ''; + let maxVisits = 0; + + for (const directorId in directorVisits) { + if (directorVisits[directorId].count > maxVisits) { + mostVisitedDirector = directorVisits[directorId].name; + maxVisits = directorVisits[directorId].count; + } + } + + return mostVisitedDirector || 'Not Available'; +} + +function getTriviaAccuracy() { + let triviaStats = JSON.parse(localStorage.getItem('triviaStats')) || { totalCorrect: 0, totalAttempted: 0 }; + if (triviaStats.totalAttempted === 0) { + return 'No trivia attempted'; + } + let accuracy = (triviaStats.totalCorrect / triviaStats.totalAttempted) * 100; + return `${accuracy.toFixed(1)}% accuracy`; +} + +function getMostCommonGenre() { + const favoriteGenres = JSON.parse(localStorage.getItem('favoriteGenres')) || {}; + let mostCommonGenre = ''; + let maxCount = 0; + + for (const genre in favoriteGenres) { + if (favoriteGenres[genre] > maxCount) { + mostCommonGenre = genre; + maxCount = favoriteGenres[genre]; + } + } + + return mostCommonGenre || 'Not Available'; +} + +document.addEventListener('DOMContentLoaded', rotateUserStats); + +async function fetchMovieDetails(movieId) { + const favoritesContainer = document.getElementById('favorites-main'); + const code = 'c5a20c861acf7bb8d9e987dcc7f1b558'; + const url = `https://api.themoviedb.org/3/movie/${movieId}?api_key=${code}&append_to_response=credits,keywords,similar`; + + try { + const response = await fetch(url); + const movie = await response.json(); + const movieVoteAverage = movie.vote_average.toFixed(1); + const movieEl = document.createElement('div'); + movieEl.classList.add('movie'); + movieEl.innerHTML = ` + ${movie.title} +
+

${movie.title}

+ ${movieVoteAverage} +
+
+

Movie Overview:

+ ${movie.overview} +
`; + + movieEl.addEventListener('click', () => { + localStorage.setItem('selectedMovieId', movie.id); + window.location.href = 'movie-details.html'; + updateMovieVisitCount(movie.id, movie.title); + }); + + favoritesContainer.appendChild(movieEl); + + } + catch (error) { + console.error('Error fetching movie details:', error); + favoritesContainer.innerHTML += '

Error loading movie details.

'; + } +} + +async function showMovieOfTheDay() { + const year = new Date().getFullYear(); + const url = `https://api.themoviedb.org/3/discover/movie?api_key=c5a20c861acf7bb8d9e987dcc7f1b558&sort_by=vote_average.desc&vote_count.gte=100&primary_release_year=${year}&vote_average.gte=7`; + + try { + const response = await fetch(url); + const data = await response.json(); + const movies = data.results; + + if (movies.length > 0) { + const randomMovie = movies[Math.floor(Math.random() * movies.length)]; + localStorage.setItem('selectedMovieId', randomMovie.id); + window.location.href = 'movie-details.html'; + } + else { + fallbackMovieSelection(); + } + } + catch (error) { + console.error('Error fetching movie:', error); + fallbackMovieSelection(); + } +} + +function fallbackMovieSelection() { + const fallbackMovies = [432413, 299534, 1726, 562, 118340, 455207, 493922, 447332, 22970, 530385, 27205, 264660, 120467, 603, 577922, 76341, 539, 419704, 515001, 118340, 424, 98]; + const randomFallbackMovie = fallbackMovies[Math.floor(Math.random() * fallbackMovies.length)]; + localStorage.setItem('selectedMovieId', randomFallbackMovie); + window.location.href = 'movie-details.html'; +} + +function openModal(modalId) { + document.getElementById(modalId).style.display = 'block'; +} + +function closeModal(modalId) { + document.getElementById(modalId).style.display = 'none'; +} + +document.getElementById('delete-watchlist-btn').addEventListener('click', () => openModal('delete-watchlist-modal')); + +async function getMovieTitle(movieId) { + const apiKey = 'c5a20c861acf7bb8d9e987dcc7f1b558'; + const url = `https://api.themoviedb.org/3/movie/${movieId}?api_key=${apiKey}`; + + try { + const response = await fetch(url); + const movie = await response.json(); + return movie.title; + } + catch (error) { + console.error('Error fetching movie title:', error); + return 'Unknown Movie'; + } +} + +async function populateCreateModalWithFavorites() { + const favorites = JSON.parse(localStorage.getItem('favorites')) || []; + const createForm = document.getElementById('create-watchlist-form'); + + let moviesContainer = document.getElementById('movies-container'); + let moviesLabel = document.getElementById('movies-label-create'); + + if (moviesContainer) { + moviesContainer.innerHTML = ''; + } + else { + moviesContainer = document.createElement('div'); + moviesContainer.id = 'movies-container'; + createForm.insertBefore(moviesContainer, createForm.querySelector('button[type="submit"]')); + } + + if (!moviesLabel) { + moviesLabel = document.createElement('label'); + moviesLabel.id = 'movies-label-create'; + moviesLabel.textContent = 'Select Movies:'; + moviesLabel.htmlFor = 'movies-container'; + createForm.insertBefore(moviesLabel, moviesContainer); + } + + for (const movieId of favorites) { + const movieTitle = await getMovieTitle(movieId); + const movieItem = document.createElement('div'); + movieItem.classList.add('movie-item'); + + const checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; + checkbox.id = `movie-${movieId}`; + checkbox.value = movieId; + checkbox.name = "favoritedMovies"; + + const label = document.createElement('label'); + label.htmlFor = `movie-${movieId}`; + label.textContent = movieTitle; + + movieItem.appendChild(checkbox); + movieItem.appendChild(label); + moviesContainer.appendChild(movieItem); + } +} + +document.getElementById('create-watchlist-btn').addEventListener('click', function() { + document.getElementById('create-watchlist-form').reset(); + populateCreateModalWithFavorites(); + openModal('create-watchlist-modal'); + updateWatchlistsCreated(); +}); + +function generateUniqueId() { + return Date.now().toString(36) + Math.random().toString(36).substr(2); +} + +document.getElementById('create-watchlist-form').addEventListener('submit', function(e) { + e.preventDefault(); + const name = document.getElementById('new-watchlist-name').value; + const description = document.getElementById('new-watchlist-description').value; + const selectedMovies = Array.from(document.querySelectorAll('#movies-container input[name="favoritedMovies"]:checked')).map(checkbox => checkbox.value); + + let watchlists = JSON.parse(localStorage.getItem('watchlists')) || []; + const uniqueId = generateUniqueId(); + watchlists.push({ + id: uniqueId, + name, + description, + movies: selectedMovies, + pinned: false, + createdAt: new Date().toISOString() + }); + + localStorage.setItem('watchlists', JSON.stringify(watchlists)); + closeModal('create-watchlist-modal'); + updateWatchListsDisplay(); + window.location.reload(); +}); + +document.getElementById('edit-watchlist-btn').addEventListener('click', async function() { + await populateEditModal(); + openModal('edit-watchlist-modal'); +}); + +async function populateEditModal() { + const watchlists = JSON.parse(localStorage.getItem('watchlists')) || []; + const favorites = JSON.parse(localStorage.getItem('favorites')) || []; + const editForm = document.getElementById('edit-watchlist-form'); + editForm.innerHTML = ''; + + const selectLabel = document.createElement('label'); + selectLabel.textContent = 'Select A Watch List:'; + selectLabel.setAttribute("for", "watchlist-select"); + editForm.appendChild(selectLabel); + + const select = document.createElement('select'); + select.id = 'watchlist-select'; + select.style.font = 'inherit'; + watchlists.forEach((watchlist, index) => { + const option = document.createElement('option'); + option.value = index; + option.textContent = watchlist.name; + select.appendChild(option); + }); + + const nameLabel = document.createElement('label'); + nameLabel.textContent = 'Watch List Name:'; + + const nameInput = document.createElement('input'); + nameInput.type = 'text'; + nameInput.id = 'edit-watchlist-name'; + nameInput.style.font = 'inherit'; + + const descLabel = document.createElement('label'); + descLabel.textContent = 'Description:'; + + const descInput = document.createElement('textarea'); + descInput.id = 'edit-watchlist-description'; + descInput.style.font = 'inherit'; + + const moviesContainer = document.createElement('div'); + moviesContainer.id = 'edit-movies-container'; + + const moviesLabel = document.createElement('label'); + moviesLabel.textContent = 'Select Movies:'; + + editForm.appendChild(select); + editForm.appendChild(nameLabel); + editForm.appendChild(nameInput); + editForm.appendChild(descLabel); + editForm.appendChild(descInput); + editForm.appendChild(moviesLabel); + editForm.appendChild(moviesContainer); + + const updateForm = async (watchlist) => { + nameInput.value = watchlist.name; + descInput.value = watchlist.description; + moviesContainer.innerHTML = ''; + + for (const movieId of favorites) { + const movieTitle = await getMovieTitle(movieId); + const isChecked = watchlist.movies.includes(movieId.toString()); + + const movieItem = document.createElement('div'); + movieItem.classList.add('movie-item'); + + const checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; + checkbox.id = `edit-movie-${movieId}`; + checkbox.value = movieId; + checkbox.checked = isChecked; + + const label = document.createElement('label'); + label.htmlFor = `edit-movie-${movieId}`; + label.textContent = movieTitle; + + movieItem.appendChild(checkbox); + movieItem.appendChild(label); + moviesContainer.appendChild(movieItem); + } + }; + + select.addEventListener('change', function() { + updateForm(watchlists[this.value]); + }); + + if (watchlists.length > 0) { + updateForm(watchlists[0]); + } + + const submitButton = document.createElement('button'); + submitButton.type = 'submit'; + submitButton.textContent = 'Save Changes'; + editForm.appendChild(submitButton); +} + +document.getElementById('edit-watchlist-form').addEventListener('submit', function(e) { + e.preventDefault(); + const selectedIndex = document.getElementById('watchlist-select').value; + const newName = document.getElementById('edit-watchlist-name').value; + const newDescription = document.getElementById('edit-watchlist-description').value; + const selectedMovies = Array.from(document.querySelectorAll('#edit-movies-container input[type="checkbox"]:checked')).map(checkbox => checkbox.value); + + let watchlists = JSON.parse(localStorage.getItem('watchlists')) || []; + watchlists[selectedIndex] = { + id: watchlists[selectedIndex].id, + name: newName, + description: newDescription, + movies: selectedMovies, + pinned: watchlists[selectedIndex].pinned + }; + + localStorage.setItem('watchlists', JSON.stringify(watchlists)); + closeModal('edit-watchlist-modal'); + updateWatchListsDisplay(); + window.location.reload(); +}); + +function populateDeleteModal() { + const watchlists = JSON.parse(localStorage.getItem('watchlists')) || []; + const deleteForm = document.getElementById('delete-watchlist-form'); + deleteForm.innerHTML = ''; + + const select = document.createElement('select'); + select.id = 'delete-watchlist-select'; + select.style.font = 'inherit'; + watchlists.forEach((watchlist, index) => { + const option = document.createElement('option'); + option.value = index; + option.textContent = watchlist.name; + select.appendChild(option); + }); + + const watchlistLabel = document.createElement('label'); + watchlistLabel.textContent = 'Select Watch List to Delete:'; + watchlistLabel.htmlFor = 'delete-watchlist-select'; + deleteForm.appendChild(watchlistLabel); + deleteForm.appendChild(select); + + const deleteButton = document.createElement('button'); + deleteButton.type = 'button'; + deleteButton.textContent = 'Delete'; + deleteForm.appendChild(deleteButton); + + deleteButton.addEventListener('click', function() { + let watchlists = JSON.parse(localStorage.getItem('watchlists')) || []; + const selectedIndex = select.value; + const watchListName = watchlists[selectedIndex].name; + if (!confirm('Are you sure you want to delete the watch list titled "' + watchListName + '"?')) return; + watchlists.splice(selectedIndex, 1); + localStorage.setItem('watchlists', JSON.stringify(watchlists)); + closeModal('delete-watchlist-modal'); + updateWatchListsDisplay(); + window.location.reload(); + }); +} + +document.getElementById('delete-watchlist-btn').addEventListener('click', populateDeleteModal); + +async function updateWatchListsDisplay() { + const watchlists = JSON.parse(localStorage.getItem('watchlists')) || []; + const displaySection = document.getElementById('watchlists-display-section'); + displaySection.innerHTML = ''; + + if (watchlists.length === 0) { + displaySection.innerHTML = '

No watch lists found. Click on "Create a Watch List" to start adding movies.

'; + } + else { + for (const watchlist of watchlists) { + const watchlistDiv = document.createElement('div'); + watchlistDiv.className = 'watchlist'; + + const title = document.createElement('h3'); + title.textContent = watchlist.name; + title.className = 'watchlist-title'; + + const description = document.createElement('p'); + description.textContent = watchlist.description; + description.className = 'watchlist-description'; + + watchlistDiv.appendChild(title); + watchlistDiv.appendChild(description); + + if (watchlist.movies.length === 0) { + const noMoviesMessage = document.createElement('p'); + noMoviesMessage.textContent = 'This watch list is empty. Use the "Edit a Watch List" button to add movies.'; + noMoviesMessage.style.textAlign = 'center'; + watchlistDiv.appendChild(noMoviesMessage); + } + else { + const moviesContainer = document.createElement('div'); + moviesContainer.className = 'movies-container'; + moviesContainer.style.flexWrap = 'wrap'; + + for (const movieId of watchlist.movies) { + const movieCard = await fetchMovieDetails(movieId); + moviesContainer.appendChild(movieCard); + } + watchlistDiv.appendChild(moviesContainer); + } + addWatchListControls(watchlistDiv); + displaySection.appendChild(watchlistDiv); + } + } +} + +async function fetchMovieDetails(movieId) { + const code = 'c5a20c861acf7bb8d9e987dcc7f1b558'; + const url = `https://api.themoviedb.org/3/movie/${movieId}?api_key=${code}&append_to_response=credits,keywords,similar`; + + try { + const response = await fetch(url); + const movie = await response.json(); + return createMovieCard(movie); + } + catch (error) { + console.error('Error fetching movie details:', error); + const errorDiv = document.createElement('div'); + errorDiv.textContent = 'Error loading movie details.'; + return errorDiv; + } +} + +function createMovieCard(movie) { + const movieEl = document.createElement('div'); + movieEl.classList.add('movie'); + movieEl.style.cursor = 'pointer'; + + movieEl.innerHTML = ` + ${movie.title} +
+

${movie.title}

+ ${movie.vote_average.toFixed(1)} +
+
+

Movie Overview:

+ ${movie.overview} +
`; + movieEl.addEventListener('click', () => { + localStorage.setItem('selectedMovieId', movie.id); + window.location.href = 'movie-details.html'; + updateMovieVisitCount(movie.id, movie.title); + }); + + return movieEl; +} + +const searchForm = document.getElementById('form'); +const searchBtn = document.getElementById('button-search'); +const searchResultsMain = document.getElementById('search-results'); +const altTitle = document.getElementById('search-title'); + +function getClassByRate(vote){ + if (vote >= 8) { + return 'green'; + } + else if (vote >= 5) { + return 'orange'; + } + else { + return 'red'; + } +} + +searchForm.addEventListener('submit', (e) => { + e.preventDefault(); + const searchTerm = search.value.trim(); + + if (searchTerm) { + getMovies(SEARCHPATH + searchTerm); + altTitle.innerHTML = 'Search Results for: ' + searchTerm; + document.getElementById('search-container').style.display = 'flex'; + search.value = ''; + } + else { + altTitle.innerHTML = 'Please enter a search term.'; + document.getElementById('search-container').style.display = 'none'; + } +}); + +searchBtn.addEventListener('click', (e) => { + e.preventDefault(); + const searchTerm = search.value; + + if (searchTerm) { + getMovies(SEARCHPATH + searchTerm); + altTitle.innerHTML = 'Search Results for: ' + searchTerm; + document.getElementById('search-container').style.display = 'flex'; + search.value = ''; + } + else { + altTitle.innerHTML = 'Please enter a search term.'; + document.getElementById('search-container').style.display = 'none'; + } +}); + +async function getMovies(url) { + const numberOfMovies = calculateMoviesToDisplay(); + const pagesToFetch = numberOfMovies <= 20 ? 1 : 2; + let allMovies = []; + + for (let page = 1; page <= pagesToFetch; page++) { + const response = await fetch(`${url}&page=${page}`); + const data = await response.json(); + allMovies = allMovies.concat(data.results); + } + + const popularityThreshold = 0.5; + + allMovies.sort((a, b) => { + const popularityDifference = Math.abs(a.popularity - b.popularity); + if (popularityDifference < popularityThreshold) { + return b.vote_average - a.vote_average; + } + return b.popularity - a.popularity; + }); + + if (allMovies.length > 0) { + showMovies(allMovies.slice(0, numberOfMovies)); + document.getElementById('clear-search-btn').style.display = 'block'; + } + else { + searchResultsMain.innerHTML = `

No movie with the specified search term found. Please try again.

`; + document.getElementById('clear-search-btn').style.display = 'none'; + } +} + +function showMovies(movies){ + searchResultsMain.innerHTML = ''; + movies.forEach((movie) => { + const { id, poster_path, title, vote_average, overview } = movie; + const movieE1 = document.createElement('div'); + const voteAverage = vote_average.toFixed(1); + movieE1.classList.add('movie'); + + const movieImage = poster_path + ? `${title}` + : `
Image Not Available
`; + + movieE1.innerHTML = ` + ${movieImage} +
+

${title}

+ ${voteAverage} +
+
+

Movie Overview:

+ ${overview} +
`; + + movieE1.addEventListener('click', () => { + localStorage.setItem('selectedMovieId', id); + window.location.href = 'movie-details.html'; + updateMovieVisitCount(id, title); + }); + + searchResultsMain.appendChild(movieE1); + }); +} + +document.getElementById('clear-search-btn').addEventListener('click', () => { + location.reload(); +}); + +function calculateMoviesToDisplay() { + const screenWidth = window.innerWidth; + if (screenWidth <= 689.9) return 10; + if (screenWidth <= 1021.24) return 20; + if (screenWidth <= 1353.74) return 21; + if (screenWidth <= 1684.9) return 20; + if (screenWidth <= 2017.49) return 20; + if (screenWidth <= 2349.99) return 18; + if (screenWidth <= 2681.99) return 21; + if (screenWidth <= 3014.49) return 24; + if (screenWidth <= 3345.99) return 27; + if (screenWidth <= 3677.99) return 20; + if (screenWidth <= 4009.99) return 22; + if (screenWidth <= 4340.99) return 24; + if (screenWidth <= 4673.49) return 26; + if (screenWidth <= 5005.99) return 28; + if (screenWidth <= 5337.99) return 30; + if (screenWidth <= 5669.99) return 32; + if (screenWidth <= 6001.99) return 34; + if (screenWidth <= 6333.99) return 36; + if (screenWidth <= 6665.99) return 38; + if (screenWidth <= 6997.99) return 40; + if (screenWidth <= 7329.99) return 42; + if (screenWidth <= 7661.99) return 44; + if (screenWidth <= 7993.99) return 46; + if (screenWidth <= 8325.99) return 48; + return 20; +} + +function handleSignInOut() { + const isSignedIn = JSON.parse(localStorage.getItem('isSignedIn')) || false; + + if (isSignedIn) { + localStorage.setItem('isSignedIn', JSON.stringify(false)); + alert('You have been signed out.'); + } + else { + window.location.href = 'sign-in.html'; + return; + } + + updateSignInButtonState(); +} + +function updateSignInButtonState() { + const isSignedIn = JSON.parse(localStorage.getItem('isSignedIn')) || false; + + const signInText = document.getElementById('signInOutText'); + const signInIcon = document.getElementById('signInIcon'); + const signOutIcon = document.getElementById('signOutIcon'); + + if (isSignedIn) { + signInText.textContent = 'Sign Out'; + signInIcon.style.display = 'none'; + signOutIcon.style.display = 'inline-block'; + } + else { + signInText.textContent = 'Sign In'; + signInIcon.style.display = 'inline-block'; + signOutIcon.style.display = 'none'; + } +} + +document.addEventListener("DOMContentLoaded", function() { + updateSignInButtonState(); + document.getElementById('googleSignInBtn').addEventListener('click', handleSignInOut); +}); + +async function loadWatchLists() { + const watchlists = JSON.parse(localStorage.getItem('watchlists')) || []; + const displaySection = document.getElementById('watchlists-display-section'); + + if (watchlists.length === 0) { + displaySection.innerHTML = '

No watch lists found. Click on "Create a Watch List" to start adding movies.

'; + } + else { + watchlists.sort((a, b) => (b.pinned === a.pinned) ? 0 : b.pinned ? 1 : -1); + + for (const watchlist of watchlists) { + const watchlistDiv = await createWatchListDiv(watchlist); + if (watchlist.pinned) { + watchlistDiv.classList.add('pinned'); + } + displaySection.appendChild(watchlistDiv); + } + } + + const favorites = JSON.parse(localStorage.getItem('favorites')) || []; + if (favorites.length > 0) { + const favoritesDiv = document.createElement('div'); + favoritesDiv.className = 'watchlist'; + favoritesDiv.id = 'favorites-watchlist'; + + const title = document.createElement('h3'); + title.textContent = "Favorited Movies"; + title.className = 'watchlist-title'; + + const description = document.createElement('p'); + description.textContent = "A collection of your favorited movies."; + description.className = 'watchlist-description'; + + favoritesDiv.appendChild(title); + favoritesDiv.appendChild(description); + + const moviesContainer = document.createElement('div'); + moviesContainer.className = 'movies-container'; + + for (const movieId of favorites) { + const movieCard = await fetchMovieDetails(movieId); + moviesContainer.appendChild(movieCard); + } + + favoritesDiv.appendChild(moviesContainer); + displaySection.appendChild(favoritesDiv); + } + else { + const favoritesMain = document.getElementById('favorites-watchlist'); + favoritesMain.innerHTML = '

No favorites added yet.

'; + window.location.reload(); + } +} + +function isListPinned(watchlistId) { + const watchlists = JSON.parse(localStorage.getItem('watchlists')) || []; + const watchlist = watchlists.find(watchlist => watchlist.id === watchlistId); + return watchlist.pinned; +} + +function addWatchListControls(watchlistDiv, watchlistId) { + const controlContainer = document.createElement('div'); + controlContainer.className = 'watchlist-controls'; + + const pinBtn = document.createElement('button'); + pinBtn.id = 'pin-btn'; + pinBtn.innerHTML = ''; + pinBtn.onclick = function() { pinWatchList(watchlistDiv, watchlistId); }; + pinBtn.title = isListPinned(watchlistId) ? 'Unpin this watch list' : 'Pin this watch list'; + + const moveUpBtn = document.createElement('button'); + moveUpBtn.innerHTML = ''; + moveUpBtn.onclick = function() { moveWatchList(watchlistDiv, true); }; + moveUpBtn.title = 'Move this watch list up'; + + const moveDownBtn = document.createElement('button'); + moveDownBtn.innerHTML = ''; + moveDownBtn.onclick = function() { moveWatchList(watchlistDiv, false); }; + moveDownBtn.title = 'Move this watch list down'; + + controlContainer.appendChild(pinBtn); + controlContainer.appendChild(moveUpBtn); + controlContainer.appendChild(moveDownBtn); + + watchlistDiv.appendChild(controlContainer); +} + +function createWatchListDiv(watchlist) { + const watchlistDiv = document.createElement('div'); + watchlistDiv.className = 'watchlist'; + watchlistDiv.setAttribute('data-watchlist-id', watchlist.id); + + const title = document.createElement('h3'); + title.textContent = watchlist.name; + title.className = 'watchlist-title'; + + const description = document.createElement('p'); + description.textContent = watchlist.description; + description.className = 'watchlist-description'; + + watchlistDiv.appendChild(title); + watchlistDiv.appendChild(description); + + const moviesContainer = document.createElement('div'); + moviesContainer.className = 'movies-container'; + moviesContainer.style.flexWrap = 'wrap'; + watchlist.movies.forEach(movieId => { + fetchMovieDetails(movieId).then(movieCard => moviesContainer.appendChild(movieCard)); + }); + watchlistDiv.appendChild(moviesContainer); + + addWatchListControls(watchlistDiv, watchlist.id); + + return watchlistDiv; +} + +function updateWatchlistsOrderInLS() { + const watchlistsDivs = document.querySelectorAll('#watchlists-display-section > .watchlist'); + let watchlists = JSON.parse(localStorage.getItem('watchlists')) || []; + const newOrder = Array.from(watchlistsDivs).map(div => div.getAttribute('data-watchlist-id')); + + watchlists.sort((a, b) => newOrder.indexOf(a.id) - newOrder.indexOf(b.id)); + localStorage.setItem('watchlists', JSON.stringify(watchlists)); +} + +function moveWatchList(watchlistDiv, moveUp) { + const sibling = moveUp ? watchlistDiv.previousElementSibling : watchlistDiv.nextElementSibling; + if (sibling) { + const parent = watchlistDiv.parentNode; + if (moveUp) { + parent.insertBefore(watchlistDiv, sibling); + } + else { + parent.insertBefore(sibling, watchlistDiv); + } + updateWatchlistsOrderInLS(); + updateWatchListsDisplay(); + } +} + +function pinWatchList(watchlistDiv, watchlistId) { + const isPinned = watchlistDiv.classList.contains('pinned'); + let watchlists = JSON.parse(localStorage.getItem('watchlists')) || []; + + watchlists.forEach(watchlist => { + if (watchlist.id === watchlistId) { + watchlist.pinned = !isPinned; + } + }); + + watchlists.sort((a, b) => (b.pinned === a.pinned) ? 0 : b.pinned ? -1 : 1); + + localStorage.setItem('watchlists', JSON.stringify(watchlists)); + watchlistDiv.classList.toggle('pinned'); + loadWatchLists(); + window.location.reload(); +} + +document.getElementById('settings-btn').addEventListener('click', () => { + window.location.href = 'settings.html'; +}); + +document.addEventListener('DOMContentLoaded', () => { + applySettings(); + + function applySettings() { + const savedBg = localStorage.getItem('backgroundImage'); + const savedTextColor = localStorage.getItem('textColor'); + const savedFontSize = localStorage.getItem('fontSize'); + + if (savedBg) { + document.body.style.backgroundImage = `url('${savedBg}')`; + } + if (savedTextColor) { + document.querySelectorAll('h1, h2, h3, p, a, span, div, button, input, select, textarea, label, li').forEach(element => { + element.style.color = savedTextColor; + }); + } + if (savedFontSize) { + const size = savedFontSize === 'small' ? '12px' : savedFontSize === 'medium' ? '16px' : '20px'; + document.body.style.fontSize = size; + } + } +}); + +function updateWatchlistsCreated() { + let watchlistsCount = parseInt(localStorage.getItem('watchlistsCreated')) || 0; + watchlistsCount++; + localStorage.setItem('watchlistsCreated', watchlistsCount.toString()); +} diff --git a/MovieVerse-Mobile/www/src/swift/Movie.swift b/MovieVerse-Mobile/platforms/ios/www/src/swift/Movie.swift similarity index 100% rename from MovieVerse-Mobile/www/src/swift/Movie.swift rename to MovieVerse-Mobile/platforms/ios/www/src/swift/Movie.swift diff --git a/MovieVerse-Mobile/www/src/swift/MovieDetailViewController.swift b/MovieVerse-Mobile/platforms/ios/www/src/swift/MovieDetailViewController.swift similarity index 100% rename from MovieVerse-Mobile/www/src/swift/MovieDetailViewController.swift rename to MovieVerse-Mobile/platforms/ios/www/src/swift/MovieDetailViewController.swift diff --git a/MovieVerse-Mobile/www/src/swift/MovieDetailsView.swift b/MovieVerse-Mobile/platforms/ios/www/src/swift/MovieDetailsView.swift similarity index 100% rename from MovieVerse-Mobile/www/src/swift/MovieDetailsView.swift rename to MovieVerse-Mobile/platforms/ios/www/src/swift/MovieDetailsView.swift diff --git a/MovieVerse-Mobile/www/src/swift/MovieFetcher.swift b/MovieVerse-Mobile/platforms/ios/www/src/swift/MovieFetcher.swift similarity index 100% rename from MovieVerse-Mobile/www/src/swift/MovieFetcher.swift rename to MovieVerse-Mobile/platforms/ios/www/src/swift/MovieFetcher.swift diff --git a/MovieVerse-Mobile/www/src/swift/MovieListView.swift b/MovieVerse-Mobile/platforms/ios/www/src/swift/MovieListView.swift similarity index 100% rename from MovieVerse-Mobile/www/src/swift/MovieListView.swift rename to MovieVerse-Mobile/platforms/ios/www/src/swift/MovieListView.swift diff --git a/MovieVerse-Mobile/www/src/swift/MovieMatchController.swift b/MovieVerse-Mobile/platforms/ios/www/src/swift/MovieMatchController.swift similarity index 100% rename from MovieVerse-Mobile/www/src/swift/MovieMatchController.swift rename to MovieVerse-Mobile/platforms/ios/www/src/swift/MovieMatchController.swift diff --git a/MovieVerse-Mobile/www/src/swift/MovieSearchView.swift b/MovieVerse-Mobile/platforms/ios/www/src/swift/MovieSearchView.swift similarity index 100% rename from MovieVerse-Mobile/www/src/swift/MovieSearchView.swift rename to MovieVerse-Mobile/platforms/ios/www/src/swift/MovieSearchView.swift diff --git a/MovieVerse-Mobile/www/src/swift/MovieTimelineViewController.swift b/MovieVerse-Mobile/platforms/ios/www/src/swift/MovieTimelineViewController.swift similarity index 100% rename from MovieVerse-Mobile/www/src/swift/MovieTimelineViewController.swift rename to MovieVerse-Mobile/platforms/ios/www/src/swift/MovieTimelineViewController.swift diff --git a/MovieVerse-Mobile/www/src/swift/MovieTriviaViewController.swift b/MovieVerse-Mobile/platforms/ios/www/src/swift/MovieTriviaViewController.swift similarity index 100% rename from MovieVerse-Mobile/www/src/swift/MovieTriviaViewController.swift rename to MovieVerse-Mobile/platforms/ios/www/src/swift/MovieTriviaViewController.swift diff --git a/MovieVerse-Mobile/www/src/swift/MoviesViewController.swift b/MovieVerse-Mobile/platforms/ios/www/src/swift/MoviesViewController.swift similarity index 100% rename from MovieVerse-Mobile/www/src/swift/MoviesViewController.swift rename to MovieVerse-Mobile/platforms/ios/www/src/swift/MoviesViewController.swift diff --git a/MovieVerse-Mobile/www/src/swift/NetworkManager.swift b/MovieVerse-Mobile/platforms/ios/www/src/swift/NetworkManager.swift similarity index 100% rename from MovieVerse-Mobile/www/src/swift/NetworkManager.swift rename to MovieVerse-Mobile/platforms/ios/www/src/swift/NetworkManager.swift diff --git a/MovieVerse-Mobile/www/src/swift/OfflineManager.swift b/MovieVerse-Mobile/platforms/ios/www/src/swift/OfflineManager.swift similarity index 100% rename from MovieVerse-Mobile/www/src/swift/OfflineManager.swift rename to MovieVerse-Mobile/platforms/ios/www/src/swift/OfflineManager.swift diff --git a/MovieVerse-Mobile/www/src/react/FeaturedMoviesCarousel.jsx b/MovieVerse-Mobile/www/react-native/FeaturedMoviesCarousel.jsx similarity index 100% rename from MovieVerse-Mobile/www/src/react/FeaturedMoviesCarousel.jsx rename to MovieVerse-Mobile/www/react-native/FeaturedMoviesCarousel.jsx diff --git a/MovieVerse-Mobile/www/src/react/GenreMovies.jsx b/MovieVerse-Mobile/www/react-native/GenreMovies.jsx similarity index 100% rename from MovieVerse-Mobile/www/src/react/GenreMovies.jsx rename to MovieVerse-Mobile/www/react-native/GenreMovies.jsx diff --git a/MovieVerse-Mobile/www/src/react/MovieDetails.jsx b/MovieVerse-Mobile/www/react-native/MovieDetails.jsx similarity index 100% rename from MovieVerse-Mobile/www/src/react/MovieDetails.jsx rename to MovieVerse-Mobile/www/react-native/MovieDetails.jsx diff --git a/MovieVerse-Mobile/www/src/react/MovieList.jsx b/MovieVerse-Mobile/www/react-native/MovieList.jsx similarity index 100% rename from MovieVerse-Mobile/www/src/react/MovieList.jsx rename to MovieVerse-Mobile/www/react-native/MovieList.jsx diff --git a/MovieVerse-Mobile/www/src/react/MovieRecommendations.jsx b/MovieVerse-Mobile/www/react-native/MovieRecommendations.jsx similarity index 100% rename from MovieVerse-Mobile/www/src/react/MovieRecommendations.jsx rename to MovieVerse-Mobile/www/react-native/MovieRecommendations.jsx diff --git a/MovieVerse-Mobile/www/src/react/MovieSearch.jsx b/MovieVerse-Mobile/www/react-native/MovieSearch.jsx similarity index 100% rename from MovieVerse-Mobile/www/src/react/MovieSearch.jsx rename to MovieVerse-Mobile/www/react-native/MovieSearch.jsx diff --git a/MovieVerse-Mobile/www/src/react/README.md b/MovieVerse-Mobile/www/react-native/README.md similarity index 100% rename from MovieVerse-Mobile/www/src/react/README.md rename to MovieVerse-Mobile/www/react-native/README.md diff --git a/MovieVerse-Mobile/www/src/react/UserProfile.jsx b/MovieVerse-Mobile/www/react-native/UserProfile.jsx similarity index 100% rename from MovieVerse-Mobile/www/src/react/UserProfile.jsx rename to MovieVerse-Mobile/www/react-native/UserProfile.jsx diff --git a/MovieVerse-Mobile/www/src/react/UserReviews.jsx b/MovieVerse-Mobile/www/react-native/UserReviews.jsx similarity index 100% rename from MovieVerse-Mobile/www/src/react/UserReviews.jsx rename to MovieVerse-Mobile/www/react-native/UserReviews.jsx diff --git a/MovieVerse-Mobile/www/src/react/index.jsx b/MovieVerse-Mobile/www/react-native/index.jsx similarity index 100% rename from MovieVerse-Mobile/www/src/react/index.jsx rename to MovieVerse-Mobile/www/react-native/index.jsx diff --git a/MovieVerse-Mobile/www/swift/Movie.swift b/MovieVerse-Mobile/www/swift/Movie.swift new file mode 100644 index 00000000..e544f8b7 --- /dev/null +++ b/MovieVerse-Mobile/www/swift/Movie.swift @@ -0,0 +1,46 @@ +struct Movie: Codable { + var id: Int + var title: String + var overview: String + var posterPath: String +} + +class WatchlistManager { + private var movies: [Movie] = [] + + func addMovie(_ movie: Movie) { + movies.append(movie) + saveMovies() + } + + func removeMovie(at index: Int) { + movies.remove(at: index) + saveMovies() + } + + func getMovies() -> [Movie] { + return movies + } + + private func saveMovies() { + // Implement the logic to save the movies array to UserDefaults + var moviesData: Data? + do { + moviesData = try JSONEncoder().encode(movies) + } + catch { + print("Error encoding movies array: \(error)") + } + } + + private func loadMovies() { + // Implement the logic to load the movies array from UserDefaults + var moviesData: Data? + do { + moviesData = try JSONDecoder().decode([Movie].self, from: moviesData) + } + catch { + print("Error decoding movies array: \(error)") + } + } +} diff --git a/MovieVerse-Mobile/www/swift/MovieDetailViewController.swift b/MovieVerse-Mobile/www/swift/MovieDetailViewController.swift new file mode 100644 index 00000000..6d6c2354 --- /dev/null +++ b/MovieVerse-Mobile/www/swift/MovieDetailViewController.swift @@ -0,0 +1,445 @@ +import UIKit + +class MovieDetailViewController: UIViewController { + + @IBOutlet weak var titleLabel: UILabel! + @IBOutlet weak var overviewLabel: UILabel! + @IBOutlet weak var posterImageView: UIImageView! + + var movie: Movie? + + override func viewDidLoad() { + super.viewDidLoad() + + if let movie = movie { + titleLabel.text = movie.title + overviewLabel.text = movie.overview + loadPosterImage(from: movie.posterPath) + } + } + + private func loadPosterImage(from path: String) { + let baseUrl = "https://image.tmdb.org/t/p/w500" + guard let imageUrl = URL(string: baseUrl + path) else { + print("Invalid poster URL") + return + } + + let task = URLSession.shared.dataTask(with: imageUrl) { data, response, error in + guard let data = data, error == nil else { + print("Error fetching poster: \(error?.localizedDescription ?? "Unknown error")") + return + } + DispatchQueue.main.async { + self.posterImageView.image = UIImage(data: data) + } + } + task.resume() + } + + private func loadMovieDetail(from path: String) { + let baseUrl = "https://api.themoviedb.org/3/movie/" + guard let movieUrl = URL(string: baseUrl + path) else { + print("Invalid movie URL") + return + } + + let task = URLSession.shared.dataTask(with: movieUrl) { data, response, error in + guard let data = data, error == nil else { + print("Error fetching movie: \(error?.localizedDescription ?? "Unknown error")") + return + } + do { + let movieDetail = try JSONDecoder().decode(MovieDetail.self, from: data) + DispatchQueue.main.async { + self.titleLabel.text = movieDetail.title + self.overviewLabel.text = movieDetail.overview + self.loadPosterImage(from: movieDetail.posterPath) + } + } + catch { + print("JSON error: \(error.localizedDescription)") + } + } + task.resume() + } + + private func loadMovieCredits(from path: String) { + let baseUrl = "https://api.themoviedb.org/3/movie/" + guard let movieUrl = URL(string: baseUrl + path + "/credits") else { + print("Invalid movie URL") + return + } + + let task = URLSession.shared.dataTask(with: movieUrl) { data, response, error in + guard let data = data, error == nil else { + print("Error fetching movie: \(error?.localizedDescription ?? "Unknown error")") + return + } + do { + let movieCredits = try JSONDecoder().decode(MovieCredits.self, from: data) + DispatchQueue.main.async { + self.titleLabel.text = movieCredits.cast.name + self.overviewLabel.text = movieCredits.cast.character + self.loadPosterImage(from: movieCredits.cast.profilePath) + } + } + catch { + print("JSON error: \(error.localizedDescription)") + } + } + task.resume() + } + + private func loadMovieVideos(from path: String) { + let baseUrl = "https://api.themoviedb.org/3/movie/" + guard let movieUrl = URL(string: baseUrl + path + "/videos") else { + print("Invalid movie URL") + return + } + + let task = URLSession.shared.dataTask(with: movieUrl) { data, response, error in + guard let data = data, error == nil else { + print("Error fetching movie: \(error?.localizedDescription ?? "Unknown error")") + return + } + do { + let movieVideos = try JSONDecoder().decode(MovieVideos.self, from: data) + DispatchQueue.main.async { + self.titleLabel.text = movieVideos.key + } + } + catch { + print("JSON error: \(error.localizedDescription)") + } + } + task.resume() + } + + private func loadMovieReviews(from path: String) { + let baseUrl = "https://api.themoviedb.org/3/movie/" + guard let movieUrl = URL(string: baseUrl + path + "/reviews") else { + print("Invalid movie URL") + return + } + + let task = URLSession.shared.dataTask(with: movieUrl) { data, response, error in + guard let data = data, error == nil else { + print("Error fetching movie: \(error?.localizedDescription ?? "Unknown error")") + return + } + do { + let movieReviews = try JSONDecoder().decode(MovieReviews.self, from: data) + DispatchQueue.main.async { + self.titleLabel.text = movieReviews.author + self.overviewLabel.text = movieReviews.content + } + } + catch { + print("JSON error: \(error.localizedDescription)") + } + } + task.resume() + } + + private func loadMovieDetail(from path: String) { + let baseUrl = "https://api.themoviedb.org/3/movie/" + guard let movieUrl = URL(string: baseUrl + path) else { + print("Invalid movie URL") + return + } + + let task = URLSession.shared.dataTask(with: movieUrl) { data, response, error in + guard let data = data, error == nil else { + print("Error fetching movie: \(error?.localizedDescription ?? "Unknown error")") + return + } + do { + let movieDetail = try JSONDecoder().decode(MovieDetail.self, from: data) + DispatchQueue.main.async { + self.titleLabel.text = movieDetail.title + self.overviewLabel.text = movieDetail.overview + self.loadPosterImage(from: movieDetail.posterPath) + } + } + catch { + print("JSON error: \(error.localizedDescription)") + } + } + task.resume() + } + + private func loadMovieCredits(from path: String) { + let baseUrl = "https://api.themoviedb.org/3/movie/" + guard let movieUrl = URL(string: baseUrl + path + "/credits") else { + print("Invalid movie URL") + return + } + + let task = URLSession.shared.dataTask(with: movieUrl) { data, response, error in + guard let data = data, error == nil else { + print("Error fetching movie: \(error?.localizedDescription ?? "Unknown error")") + return + } + do { + let movieCredits = try JSONDecoder().decode(MovieCredits.self, from: data) + DispatchQueue.main.async { + self.titleLabel.text = movieCredits.cast.name + self.overviewLabel.text = movieCredits.cast.character + self.loadPosterImage(from: movieCredits.cast.profilePath) + } + } + catch { + print("JSON error: \(error.localizedDescription)") + } + } + task.resume() + } + + private func loadMovieVideos(from path: String) { + let baseUrl = "https://api.themoviedb.org/3/movie/" + guard let movieUrl = URL(string: baseUrl + path + "/videos") else { + print("Invalid movie URL") + return + } + + let task = URLSession.shared.dataTask(with: movieUrl) { data, response, error in + guard let data = data, error == nil else { + print("Error fetching movie: \(error?.localizedDescription ?? "Unknown error")") + return + } + do { + let movieVideos = try JSONDecoder().decode(MovieVideos.self, from: data) + DispatchQueue.main.async { + self.titleLabel.text = movieVideos.key + } + } + catch { + print("JSON error: \(error.localizedDescription)") + } + } + task.resume() + } + + func fetchingMovieDetail() { + guard let url = URL(string: "https://api.themoviedb.org/3/movie/550?api_key=1f54bd990f1cdfb230adb312546d765d") else { + print("Invalid URL") + return + } + let task = URLSession.shared.dataTask(with: url) { data, response, error in + guard let data = data, error == nil else { + print("Network error: \(error?.localizedDescription ?? "Unknown error")") + return + } + do { + let movieDetail = try JSONDecoder().decode(MovieDetail.self, from: data) + DispatchQueue.main.async { + self.titleLabel.text = movieDetail.title + self.overviewLabel.text = movieDetail.overview + self.loadPosterImage(from: movieDetail.posterPath) + } + } + catch { + print("JSON error: \(error.localizedDescription)") + } + } + task.resume() + } +} + +struct Movie: Codable { + let title: String + let overview: String + let posterPath: String +} + +struct MovieResults: Codable { + let results: [Movie] +} + +struct MovieDetail: Codable { + let title: String + let overview: String + let posterPath: String + let releaseDate: String + let runtime: Int + let voteAverage: Double + let genres: [Genre] +} + +struct Genre: Codable { + let name: String +} + +struct MovieDetailResults: Codable { + let results: [MovieDetail] +} + +struct MovieCredits: Codable { + let cast: [Cast] +} + +struct Cast: Codable { + let name: String + let character: String + let profilePath: String +} + +struct MovieCreditsResults: Codable { + let results: [MovieCredits] +} + +struct MovieVideos: Codable { + let key: String +} + +struct MovieVideosResults: Codable { + let results: [MovieVideos] +} + +struct MovieReviews: Codable { + let author: String + let content: String +} + +struct MovieReviewsResults: Codable { + let results: [MovieReviews] +} + +struct FavoritesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { + + @IBOutlet weak var tableView: UITableView! + + var favoriteMovies: [Movie] = [] + var movie: Movie? + + override func viewDidLoad() { + super.viewDidLoad() + tableView.delegate = self + tableView.dataSource = self + + loadFavorites() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + loadFavorites() + } + + private func loadFavorites() { + if let savedMovies = UserDefaults.standard.object(forKey: "Favorites") as? Data { + let decoder = JSONDecoder() + if let loadedMovies = try? decoder.decode([Movie].self, from: savedMovies) { + favoriteMovies = loadedMovies + tableView.reloadData() + } + } + } + + private func saveFavorites() { + let encoder = JSONEncoder() + if let encoded = try? encoder.encode(favoriteMovies) { + UserDefaults.standard.set(encoded, forKey: "Favorites") + } + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return favoriteMovies.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "FavoriteMovieCell", for: indexPath) + let movie = favoriteMovies[indexPath.row] + cell.textLabel?.text = movie.title + return cell + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + movie = favoriteMovies[indexPath.row] + performSegue(withIdentifier: "ShowFavoriteMovieDetail", sender: self) + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if segue.identifier == "ShowFavoriteMovieDetail" { + let movieDetailViewController = segue.destination as! MovieDetailViewController + movieDetailViewController.movie = movie + } + } + + func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { + if editingStyle == .delete { + favoriteMovies.remove(at: indexPath.row) + tableView.deleteRows(at: [indexPath], with: .fade) + saveFavorites() + } + } + +} + +struct MoviesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { + + @IBOutlet weak var tableView: UITableView! + + var movies: [Movie] = [] + var movie: Movie? + + override func viewDidLoad() { + super.viewDidLoad() + tableView.delegate = self + tableView.dataSource = self + + loadMovies() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + loadMovies() + } + + private func loadMovies() { + guard let url = URL(string: "https://api.themoviedb.org/3/movie/now_playing?api_key=1f54bd990f1cdfb230adb312546d765d") else { + print("Invalid URL") + return + } + let task = URLSession.shared.dataTask(with: url) { data, response, error in + guard let data = data, error == nil else { + print("Network error: \(error?.localizedDescription ?? "Unknown error")") + return + } + do { + let movieResults = try JSONDecoder().decode(MovieResults.self, from: data) + DispatchQueue.main.async { + self.movies = movieResults.results + self.tableView.reloadData() + } + } + catch { + print("JSON error: \(error.localizedDescription)") + } + } + task.resume() + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return movies.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let movie = movies[indexPath.row] + let cell = tableView.dequeueReusableCell(withIdentifier: "MovieCell", for: indexPath) + cell.textLabel?.text = movie.title + return cell + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + movie = movies[indexPath.row] + performSegue(withIdentifier: "ShowMovieDetail", sender: self) + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if segue.identifier == "ShowMovieDetail" { + let movieDetailViewController = segue.destination as! MovieDetailViewController + movieDetailViewController.movie = movie + } + } +} diff --git a/MovieVerse-Mobile/www/swift/MovieDetailsView.swift b/MovieVerse-Mobile/www/swift/MovieDetailsView.swift new file mode 100644 index 00000000..e49be084 --- /dev/null +++ b/MovieVerse-Mobile/www/swift/MovieDetailsView.swift @@ -0,0 +1,126 @@ +import SwiftUI +import Combine + +struct MovieDetailsView: View { + @ObservedObject var viewModel: MovieDetailsViewModel + let movieId: Int + + init(movieId: Int) { + self.movieId = movieId + self.viewModel = MovieDetailsViewModel(movieId: movieId) + } + + var body: some View { + ScrollView { + VStack { + if let movie = viewModel.movieDetails { + MoviePosterImage(url: movie.posterURL) + .frame(width: 200, height: 300) + .cornerRadius(8) + + Text(movie.title) + .font(.title) + .fontWeight(.bold) + .padding(.top, 10) + + Text("Rating: \(movie.voteAverage, specifier: "%.1f") / 10") + .font(.subheadline) + .padding(.vertical, 4) + + Text(movie.overview) + .font(.body) + .padding() + } else { + Text("Loading movie details...") + .font(.headline) + .foregroundColor(.gray) + } + } + } + .onAppear { + viewModel.fetchMovieDetails() + } + } +} + +struct MoviePosterImage: View { + let url: URL + + @State private var imageData: Data? + + var body: some View { + if let imageData = imageData, let uiImage = UIImage(data: imageData) { + Image(uiImage: uiImage) + .resizable() + } + else { + Rectangle() + .foregroundColor(.gray) + .onAppear { + fetchImageData() + } + } + } + + private func fetchImageData() { + URLSession.shared.dataTask(with: url) { data, _, _ in + if let data = data { + DispatchQueue.main.async { + self.imageData = data + } + } + }.resume() + } + + private func fetchImageData2() { + URLSession.shared.dataTaskPublisher(for: url) + .map { $0.data } + .replaceError(with: nil) + .receive(on: DispatchQueue.main) + .assign(to: \.imageData, on: self) + } + +} + +struct MovieDetailsViewModel { + @Published var movieDetails: MovieDetail? + + private let movieId: Int + private var cancellables = Set() + + init(movieId: Int) { + self.movieId = movieId + } + + func fetchMovieDetails() { + let url = URL(string: "https://api.themoviedb.org/3/movie/\(movieId)?api_key=YOUR_API_KEY_HERE")! + URLSession.shared.dataTaskPublisher(for: url) + .map { $0.data } + .decode(type: MovieDetail.self, decoder: JSONDecoder()) + .replaceError(with: nil) + .receive(on: DispatchQueue.main) + .assign(to: \.movieDetails, on: self) + .store(in: &cancellables) + } +} + +struct MovieDetail: Codable { + let title: String + let overview: String + let voteAverage: Double + let posterPath: String + + var posterURL: URL { + URL(string: "https://image.tmdb.org/t/p/w500\(posterPath)")! + } +} + +struct MovieDetailResults: Codable { + let results: [MovieDetail] +} + +struct MovieDetailsView_Previews: PreviewProvider { + static var previews: some View { + MovieDetailsView(movieId: 123) + } +} diff --git a/MovieVerse-Mobile/www/swift/MovieFetcher.swift b/MovieVerse-Mobile/www/swift/MovieFetcher.swift new file mode 100644 index 00000000..021ee610 --- /dev/null +++ b/MovieVerse-Mobile/www/swift/MovieFetcher.swift @@ -0,0 +1,429 @@ +import Foundation +import SwiftUI +import Combine + +struct Movie: Identifiable, Decodable { + var id: Int + var title: String + var voteAverage: Double + var overview: String + var posterPath: String? + + var posterURL: URL { + return URL(string: "https://image.tmdb.org/t/p/w500\(posterPath ?? "")")! + } + + enum CodingKeys: String, CodingKey { + case id, title, overview + case voteAverage = "vote_average" + case posterPath = "poster_path" + } +} + +struct Trivia: Identifiable, Decodable { + var id: Int + var results: [TriviaResult] +} + +struct TriviaResult: Identifiable, Decodable { + var id: Int + var question: String + var correctAnswer: String + var incorrectAnswers: [String] + + enum CodingKeys: String, CodingKey { + case id, question + case correctAnswer = "correct_answer" + case incorrectAnswers = "incorrect_answers" + } +} + +struct SearchResults: Identifiable, Decodable { + var id: Int + var results: [SearchResult] +} + +struct Favorites: Identifiable, Decodable { + var id: Int + var results: [Favorite] +} + +struct SearchResult: Identifiable, Decodable { + var id: Int + var title: String + var voteAverage: Double + var overview: String + var posterPath: String? + + var posterURL: URL { + return URL(string: "https://image.tmdb.org/t/p/w500\(posterPath ?? "")")! + } + + enum CodingKeys: String, CodingKey { + case id, title, overview + case voteAverage = "vote_average" + case posterPath = "poster_path" + } +} + +struct Favorite: Identifiable, Decodable { + var id: Int + var title: String + var voteAverage: Double + var overview: String + var posterPath: String? + + var posterURL: URL { + return URL(string: "https://image.tmdb.org/t/p/w500\(posterPath ?? "")")! + } + + enum CodingKeys: String, CodingKey { + case id, title, overview + case voteAverage = "vote_average" + case posterPath = "poster_path" + } +} + +struct MovieDetails: Identifiable, Decodable { + var id: Int + var title: String + var voteAverage: Double + var overview: String + var posterPath: String? + var backdropPath: String? + var releaseDate: String + var runtime: Int + var genres: [Genre] + var credits: Credits + var videos: Videos + var images: Images + var keywords: Keywords + var productionCompanies: [ProductionCompany] + var productionCountries: [ProductionCountry] + var spokenLanguages: [SpokenLanguage] + var revenue: Int + var budget: Int + var tagline: String + var homepage: String + var imdbId: String + var status: String + + var posterURL: URL { + return URL(string: "https://image.tmdb.org/t/p/w500\(posterPath ?? "")")! + } + + var backdropURL: URL { + return URL(string: "https://image.tmdb.org/t/p/w500\(backdropPath ?? "")")! + } + + enum CodingKeys: String, CodingKey { + case id, title, overview + case voteAverage = "vote_average" + case posterPath = "poster_path" + case backdropPath = "backdrop_path" + case releaseDate = "release_date" + case runtime, genres, credits, videos, images, keywords + case productionCompanies = "production_companies" + case productionCountries = "production_countries" + case spokenLanguages = "spoken_languages" + case revenue, budget, tagline, homepage + case imdbId = "imdb_id" + case status + } +} + +class MovieFetcher: ObservableObject { + @Published var movies = [Movie]() + @Published var movieDetails: Movie? + @Published var imageData: Data? + @Published var trivia: Trivia? + @Published var favorites = [Favorite]() + @Published var searchResults = [SearchResult]() + @Published var searchResults2 = [SearchResult]() + @Published var searchResults3 = [SearchResult]() + private var cancellables = Set() + private var cancellables2 = Set() + + private func handleCompletion(completion: Subscribers.Completion) { + switch completion { + case .finished: + print("Data fetch completed successfully.") + case .failure(let error): + print("Error occurred: \(error.localizedDescription)") + } + } + + func fetchMovies() { + let url = URL(string: "https://api.themoviedb.org/3/movie/popular?api_key=123456789")! + fetchMovies(url: url) + } + + func fetchMovies(url: URL) { + URLSession.shared.dataTaskPublisher(for: url) + .map { $0.data } + .decode(type: [Movie].self, decoder: JSONDecoder()) + .receive(on: DispatchQueue.main) + .sink(receiveCompletion: { [weak self] completion in + self?.handleCompletion(completion: completion) + }, receiveValue: { [weak self] movies in + self?.movies = movies + }) + .store(in: &cancellables) + } + + func fetchMovieDetails(url: URL) { + URLSession.shared.dataTaskPublisher(for: url) + .map { $0.data } + .decode(type: Movie.self, decoder: JSONDecoder()) + .receive(on: DispatchQueue.main) + .sink(receiveCompletion: { completion in + handleCompletion() + }, receiveValue: { [weak self] movie in + self?.movieDetails = movie + }) + .store(in: &cancellables) + } + + func fetchFavorites() { + let url = URL(string: "https://api.themoviedb.org/3/movie/popular?api_key=123456789")! + fetchFavorites(url: url) + } + + func fetchFavorites(url: URL) { + URLSession.shared.dataTaskPublisher(for: url) + .map { $0.data } + .decode(type: [Favorite].self, decoder: JSONDecoder()) + .receive(on: DispatchQueue.main) + .sink(receiveCompletion: { [weak self] completion in + self?.handleCompletion(completion: completion) + }, receiveValue: { [weak self] favorites in + self?.favorites = favorites + }) + .store(in: &cancellables) + } + + func searchMovies(query: String) { + let url = URL(string: "https://api.themoviedb.org/3/search/movie?query=\(query)&api_key=123456789")! + fetchMovies(url: url) + } + + func fetchMovieDetails(movieId: Int) { + let url = URL(string: "https://api.themoviedb.org/3/movie/\(movieId)?api_key=123456789")! + URLSession.shared.dataTaskPublisher(for: url) + .map { $0.data } + .decode(type: Movie.self, decoder: JSONDecoder()) + .receive(on: DispatchQueue.main) + .sink(receiveCompletion: { completion in + handleCompletion() + }, receiveValue: { [weak self] movie in + self?.movieDetails = movie + }) + .store(in: &cancellables) + } + + func fetchMoviesByCategory(category: String) { + let url = URL(string: "https://api.themoviedb.org/3/movie/\(category)?api_key=123456789")! + fetchMovies(url: url) + } + + func fetchTrivia() { + let url = URL(string: "https://opentdb.com/api.php?amount=1&category=11&difficulty=easy&type=multiple")! + fetchTrivia(url: url) + } + + func fetchTrivia(url: URL) { + URLSession.shared.dataTaskPublisher(for: url) + .map { $0.data } + .decode(type: Trivia.self, decoder: JSONDecoder()) + .receive(on: DispatchQueue.main) + .sink(receiveCompletion: { completion in + handleCompletion() + }, receiveValue: { [weak self] trivia in + self?.trivia = trivia + }) + .store(in: &cancellables) + } + + func fetchTriviaByCategory(category: String) { + let url = URL(string: "https://opentdb.com/api.php?amount=1&category=\(category)&difficulty=easy&type=multiple")! + fetchTrivia(url: url) + } + + func fetchImageData(from url: URL) { + URLSession.shared.dataTaskPublisher(for: url) + .map { $0.data } + .receive(on: DispatchQueue.main) + .sink(receiveCompletion: { completion in + handleCompletion() + }, receiveValue: { [weak self] data in + self?.imageData = data + }) + .store(in: &cancellables) + } + + func fetchMoviesByGenre(genre: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&with_genres=\(genre)")! + fetchMovies(url: url) + } + + func fetchMoviesByYear(year: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&primary_release_year=\(year)")! + fetchMovies(url: url) + } + + func fetchMoviesByActor(actor: String) { + let url = URL(string: "https://api.themoviedb.org/3/search/person?api_key=123456789&query=\(actor)")! + fetchMovies(url: url) + } + + func fetchMoviesByDirector(director: String) { + let url = URL(string: "https://api.themoviedb.org/3/search/person?api_key=123456789&query=\(director)")! + fetchMovies(url: url) + } + + func fetchMoviesByWriter(writer: String) { + let url = URL(string: "https://api.themoviedb.org/3/search/person?api_key=123456789&query=\(writer)")! + fetchMovies(url: url) + } + + func fetchMoviesByKeyword(keyword: String) { + let url = URL(string: "https://api.themoviedb.org/3/search/keyword?api_key=123456789&query=\(keyword)")! + fetchMovies(url: url) + } + + func fetchMoviesByCompany(company: String) { + let url = URL(string: "https://api.themoviedb.org/3/search/company?api_key=123456789&query=\(company)")! + fetchMovies(url: url) + } + + func fetchMoviesByCountry(country: String) { + let url = URL(string: "https://api.themoviedb.org/3/search/company?api_key=123456789&query=\(country)")! + fetchMovies(url: url) + } + + func fetchMoviesByLanguage(language: String) { + let url = URL(string: "https://api.themoviedb.org/3/search/company?api_key=123456789&query=\(language)")! + fetchMovies(url: url) + } + + func fetchMoviesByRuntime(runtime: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&with_runtime.lte=\(runtime)")! + fetchMovies(url: url) + } + + func fetchMoviesByRating(rating: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&vote_average.gte=\(rating)")! + fetchMovies(url: url) + } + + func fetchMoviesByRevenue(revenue: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&with_revenue.gte=\(revenue)")! + fetchMovies(url: url) + } + + func fetchMoviesByVoteCount(voteCount: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&vote_count.gte=\(voteCount)")! + fetchMovies(url: url) + } + + func fetchMoviesByCertification(certification: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&certification_country=US&certification=\(certification)")! + fetchMovies(url: url) + } + + func fetchMoviesByReleaseDate(releaseDate: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&primary_release_date.gte=\(releaseDate)")! + fetchMovies(url: url) + } + + func fetchMoviesBySort(sort: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&sort_by=\(sort)")! + fetchMovies(url: url) + } + + func fetchMoviesByAdult(adult: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&include_adult=\(adult)")! + fetchMovies(url: url) + } + + func fetchMoviesByVideo(video: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&include_video=\(video)")! + fetchMovies(url: url) + } + + func fetchMoviesByPage(page: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&page=\(page)")! + fetchMovies(url: url) + } + + func fetchMoviesByGenreAndYear(genre: String, year: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&with_genres=\(genre)&primary_release_year=\(year)")! + fetchMovies(url: url) + } + + func fetchMoviesByGenreAndActor(genre: String, actor: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&with_genres=\(genre)&with_people=\(actor)")! + fetchMovies(url: url) + } + + func fetchMoviesByGenreAndDirector(genre: String, director: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&with_genres=\(genre)&with_people=\(director)")! + fetchMovies(url: url) + } + + func fetchMoviesByGenreAndWriter(genre: String, writer: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&with_genres=\(genre)&with_people=\(writer)")! + fetchMovies(url: url) + } + + func fetchMoviesByGenreAndKeyword(genre: String, keyword: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&with_genres=\(genre)&with_keywords=\(keyword)")! + fetchMovies(url: url) + } + + func fetchMoviesByGenreAndCompany(genre: String, company: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&with_genres=\(genre)&with_companies=\(company)")! + fetchMovies(url: url) + } + + func fetchMoviesByGenreAndCountry(genre: String, country: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&with_genres=\(genre)&with_companies=\(country)")! + fetchMovies(url: url) + } + + func fetchMoviesByGenreAndLanguage(genre: String, language: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&with_genres=\(genre)&with_companies=\(language)")! + fetchMovies(url: url) + } + + func fetchMoviesByGenreAndRuntime(genre: String, runtime: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&with_genres=\(genre)&with_runtime.lte=\(runtime)")! + fetchMovies(url: url) + } + + func fetchMoviesByGenreAndRating(genre: String, rating: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&with_genres=\(genre)&vote_average.gte=\(rating)")! + fetchMovies(url: url) + } + + func fetchMoviesByGenreAndRevenue(genre: String, revenue: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&with_genres=\(genre)&with_revenue.gte=\(revenue)")! + fetchMovies(url: url) + } + + func fetchMoviesByGenreAndVoteCount(genre: String, voteCount: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&with_genres=\(genre)&vote_count.gte=\(voteCount)")! + fetchMovies(url: url) + } + + func fetchMoviesByGenreAndCertification(genre: String, certification: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&certification_country=US&certification=\(certification)&with_genres=\(genre)")! + fetchMovies(url: url) + } + + func fetchMoviesByGenreAndReleaseDate(genre: String, releaseDate: String) { + let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=123456789&primary_release_date.gte=\(releaseDate)&with_genres=\(genre)")! + fetchMovies(url: url) + } + +} diff --git a/MovieVerse-Mobile/www/swift/MovieListView.swift b/MovieVerse-Mobile/www/swift/MovieListView.swift new file mode 100644 index 00000000..6f621f64 --- /dev/null +++ b/MovieVerse-Mobile/www/swift/MovieListView.swift @@ -0,0 +1,214 @@ +import SwiftUI +import Combine + +struct MovieListView: View { + @ObservedObject var viewModel: MovieFetcher + + init(viewModel: MovieFetcher) { + self.viewModel = viewModel + } + + var body: some View { + NavigationView { + List(viewModel.movies) { movie in + NavigationLink(destination: MovieDetailsView(movieId: movie.id)) { + HStack { + MoviePosterView(url: movie.posterURL) + .frame(width: 50, height: 75) + .cornerRadius(5) + + VStack(alignment: .leading) { + Text(movie.title) + .font(.headline) + + Text("Rating: \(movie.voteAverage, specifier: "%.1f")") + .font(.subheadline) + .foregroundColor(.gray) + } + } + } + } + .navigationBarTitle("Movies") + } + .onAppear { + viewModel.fetchMovies(url: URL(string: "https://api.example.com/movies")!) // Replace with actual URL + } + } + + var view = MovieListView(viewModel: MovieFetcher()) + var body: some View { + view + } +} + +struct MoviePosterView: View { + let url: URL + + @State private var imageData: Data? + + var body: some View { + if let imageData = imageData, let uiImage = UIImage(data: imageData) { + Image(uiImage: uiImage) + .resizable() + } + else { + Rectangle() + .foregroundColor(.secondary) + .onAppear { + fetchImageData() + } + } + } + + private func fetchImageData() { + URLSession.shared.dataTask(with: url) { data, _, _ in + if let data = data { + DispatchQueue.main.async { + self.imageData = data + } + } + }.resume() + } + + func fetchMoviesList() { + guard let url = URL(string: "https://api.example.com/movies") else { + print("Invalid URL") + return + } + let task = URLSession.shared.dataTask(with: url) { data, response, error in + guard let data = data, error == nil else { + print("Network error: \(error?.localizedDescription ?? "Unknown error")") + return + } + do { + let movies = try JSONDecoder().decode([Movie].self, from: data) + DispatchQueue.main.async { + self.movies = movies + self.tableView.reloadData() + } + } + catch { + print("JSON error: \(error.localizedDescription)") + } + } + task.resume() + } + + func fetchMovie() { + guard let url = URL(string: "https://api.example.com/movies") else { + print("Invalid URL") + return + } + let task = URLSession.shared.dataTask(with: url) { data, response, error in + guard let data = data, error == nil else { + print("Network error: \(error?.localizedDescription ?? "Unknown error")") + return + } + do { + let movie = try JSONDecoder().decode(Movie.self, from: data) + DispatchQueue.main.async { + self.movie = movie + } + } + catch { + print("JSON error: \(error.localizedDescription)") + } + } + task.resume() + } +} + +struct MovieListView_Previews: PreviewProvider { + static var previews: some View { + MovieListView(viewModel: MovieFetcher()) + } +} + +struct Movie: Codable, Identifiable { + let id: Int + let title: String + let voteAverage: Double + let posterPath: String + + var posterURL: URL { + URL(string: "https://image.tmdb.org/t/p/w500\(posterPath)")! + } +} + +struct MovieResults: Codable { + let results: [Movie] +} + +class MovieFetcher: ObservableObject { + @Published var movies: [Movie] = [] + + private var cancellables = Set() + + func fetchMovies(url: URL) { + URLSession.shared.dataTaskPublisher(for: url) + .map { $0.data } + .decode(type: MovieResults.self, decoder: JSONDecoder()) + .map { $0.results } + .replaceError(with: []) + .receive(on: DispatchQueue.main) + .assign(to: \.movies, on: self) + .store(in: &cancellables) + } +} + +struct MovieDetailsView: View { + let movieId: Int + + @ObservedObject var viewModel: MovieDetailsViewModel + + init(movieId: Int) { + self.movieId = movieId + viewModel = MovieDetailsViewModel(movieId: movieId) + } + + var body: some View { + VStack(alignment: .leading) { + if let movieDetails = viewModel.movieDetails { + MoviePosterView(url: movieDetails.posterURL) + .frame(width: 150, height: 225) + .cornerRadius(5) + + Text(movieDetails.title) + .font(.title) + + Text(movieDetails.overview) + .font(.body) + .padding(.top, 10) + + Text("Rating: \(movieDetails.voteAverage, specifier: "%.1f")") + .font(.subheadline) + .foregroundColor(.gray) + .padding(.top, 10) + } + else { + Text("Loading...") + } + } + .padding() + .onAppear { + viewModel.fetchMovieDetails() + } + } +} + +struct MovieDetailsView_Previews: PreviewProvider { + static var previews: some View { + MovieDetailsView(movieId: 1) + } +} + +struct MovieDetail: Codable { + let title: String + let overview: String + let voteAverage: Double + let posterPath: String + + var posterURL: URL { + URL(string: "https://image.tmdb.org/t/p/w500\(posterPath)")! + } +} diff --git a/MovieVerse-Mobile/www/swift/MovieMatchController.swift b/MovieVerse-Mobile/www/swift/MovieMatchController.swift new file mode 100644 index 00000000..e0696549 --- /dev/null +++ b/MovieVerse-Mobile/www/swift/MovieMatchController.swift @@ -0,0 +1,332 @@ +import Foundation +import UIKit + +class MovieMatchViewController: UIViewController { + + // Properties to manage user preferences and movie data + var userPreferences: [String: Any] = [:] + var matchedMovies: [Movie] = [] + var likedMovies: [Movie] = [] + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return matchedMovies.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "MatchedMovieCell", for: indexPath) as! MatchedMovieCell + let movie = matchedMovies[indexPath.row] + cell.movieTitleLabel.text = movie.title + cell.movieYearLabel.text = movie.year + cell.movieGenreLabel.text = movie.genre + cell.moviePosterImageView.image = movie.poster + return cell + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if segue.identifier == "MatchedMovieDetailSegue" { + let movieDetailViewController = segue.destination as! MovieDetailViewController + let indexPath = matchedMoviesTableView.indexPathForSelectedRow! + let movie = matchedMovies[indexPath.row] + movieDetailViewController.movie = movie + } + } + + override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { + if identifier == "MatchedMovieDetailSegue" { + let indexPath = matchedMoviesTableView.indexPathForSelectedRow! + let movie = matchedMovies[indexPath.row] + if movie.isHidden { + return false + } + } + return true + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + matchedMoviesTableView.reloadData() + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + matchedMoviesTableView.reloadData() + } + + override func viewDidLoad() { + super.viewDidLoad() + setupView() + } + + private func setupView() { + var matchMoviesButton = UIButton() + matchMoviesButton.addTarget(self, action: #selector(matchMoviesButtonTapped), for: .touchUpInside) + var matchedMoviesTableView = UITableView() + matchedMoviesTableView.delegate = self + matchedMoviesTableView.dataSource = self + } + + @objc func matchMoviesButtonTapped() { + userPreferences = gatherUserPreferences() + fetchMatchedMovies(with: userPreferences) + } + + private func gatherUserPreferences() -> [String: Any] { + // Gather user preferences from UI elements + // Return a dictionary of preferences + ["genre": "Action", "year": "2019"] + ["genre": "Comedy", "year": "2019"] + ["genre": "Drama", "year": "2019"] + ["genre": "Horror", "year": "2019"] + ["genre": "Romance", "year": "2019"] + ["genre": "Sci-Fi", "year": "2019"] + ["genre": "Action", "year": "2019"] + ["genre": "Comedy", "year": "2019"] + ["genre": "Drama", "year": "2019"] + ["genre": "Horror", "year": "2019"] + ["genre": "Romance", "year": "2019"] + ["genre": "Sci-Fi", "year": "2019"] + ["genre": "Action", "year": "2019"] + ["genre": "Comedy", "year": "2019"] + ["genre": "Drama", "year": "2019"] + ["genre": "Horror", "year": "2019"] + ["genre": "Romance", "year": "2019"] + ["genre": "Sci-Fi", "year": "2019"] + ["genre": "Action", "year": "2019"] + ["genre": "Comedy", "year": "2019"] + ["genre": "Drama", "year": "2019"] + ["genre": "Horror", "year": "2019"] + ["genre": "Romance", "year": "2019"] + ["genre": "Sci-Fi", "year": "2019"] + return [:] + } + + private func fetchMatchedMovies(with preferences: [String: Any]) { + var matchedMovies: [Movie] = [] + matchedMovies.append(Movie(title: "Avengers: Endgame", year: "2019", genre: "Action", poster: UIImage(named: "avengers-endgame")!)) + matchedMovies.append(Movie(title: "Joker", year: "2019", genre: "Drama", poster: UIImage(named: "joker")!)) + matchedMovies.append(Movie(title: "Once Upon a Time in Hollywood", year: "2019", genre: "Comedy", poster: UIImage(named: "once-upon-a-time-in-hollywood")!)) + matchedMovies.append(Movie(title: "Us", year: "2019", genre: "Horror", poster: UIImage(named: "us")!)) + matchedMovies.append(Movie(title: "Alita: Battle Angel", year: "2019", genre: "Sci-Fi", poster: UIImage(named: "alita-battle-angel")!)) + matchedMovies.append(Movie(title: "Aladdin", year: "2019", genre: "Romance", poster: UIImage(named: "aladdin")!)) + matchedMovies.append(Movie(title: "Avengers: Endgame", year: "2019", genre: "Action", poster: UIImage(named: "avengers-endgame")!)) + matchedMovies.append(Movie(title: "Joker", year: "2019", genre: "Drama", poster: UIImage(named: "joker")!)) + matchedMovies.append(Movie(title: "Once Upon a Time in Hollywood", year: "2019", genre: "Comedy", poster: UIImage(named: "once-upon-a-time-in-hollywood")!)) + matchedMovies.append(Movie(title: "Us", year: "2019", genre: "Horror", poster: UIImage(named: "us")!)) + matchedMovies.append(Movie(title: "Alita: Battle Angel", year: "2019", genre: "Sci-Fi", poster: UIImage(named: "alita-battle-angel")!)) + matchedMovies.append(Movie(title: "Aladdin", year: "2019", genre: "Romance", poster: UIImage(named: "aladdin")!)) + matchedMovies.append(Movie(title: "Avengers: Endgame", year: "2019", genre: "Action", poster: UIImage(named: "avengers-endgame")!)) + matchedMovies.append(Movie(title: "Joker", year: "2019", genre: "Drama", poster: UIImage(named: "joker")!)) + matchedMovies.append(Movie(title: "Once Upon a Time in Hollywood", year: "2019", genre: "Comedy", poster: UIImage(named: "once-upon-a-time-in-hollywood")!)) + matchedMovies.append(Movie(title: "Us", year: "2019", genre: "Horror", poster: UIImage(named: "us")!)) + matchedMovies.append(Movie(title: "Alita: Battle Angel", year: "2019", genre: "Sci-Fi", poster: UIImage(named: "alita-battle-angel")!)) + matchedMovies.append(Movie(title: "Aladdin", year: "2019", genre: "Romance", poster: UIImage(named: "aladdin")!)) + matchedMovies.append(Movie(title: "Avengers: Endgame", year: "2019", genre: "Action", poster: UIImage(named: "avengers-endgame")!)) + matchedMovies.append(Movie(title: "Joker", year: "2019", genre: "Drama", poster: UIImage(named: "joker")!)) + matchedMovies.append(Movie(title: "Once Upon a Time in Hollywood", year: "2019", genre: "Comedy", poster: UIImage(named: "once-upon-a-time-in-hollywood")!)) + matchedMovies.append(Movie(title: "Us", year: "2019", genre: "Horror", poster: UIImage(named: "us")!)) + matchedMovies.append(Movie(title: "Alita: Battle Angel", year: "2019", genre: "Sci-Fi", poster: UIImage(named: "alita-battle-angel")!)) + matchedMovies.append(Movie(title: "Aladdin", year: "2019", genre: "Romance", poster: UIImage(named: "aladdin")!)) + self.matchedMovies = matchedMovies + } + + private func fetchLikedMovies() { + var likedMovies: [Movie] = [] + likedMovies.append(Movie(title: "Avengers: Endgame", year: "2019", genre: "Action", poster: UIImage(named: "avengers-endgame")!)) + likedMovies.append(Movie(title: "Joker", year: "2019", genre: "Drama", poster: UIImage(named: "joker")!)) + likedMovies.append(Movie(title: "Once Upon a Time in Hollywood", year: "2019", genre: "Comedy", poster: UIImage(named: "once-upon-a-time-in-hollywood")!)) + likedMovies.append(Movie(title: "Us", year: "2019", genre: "Horror", poster: UIImage(named: "us")!)) + likedMovies.append(Movie(title: "Alita: Battle Angel", year: "2019", genre: "Sci-Fi", poster: UIImage(named: "alita-battle-angel")!)) + likedMovies.append(Movie(title: "Aladdin", year: "2019", genre: "Romance", poster: UIImage(named: "aladdin")!)) + self.likedMovies = likedMovies + } + + private func fetchDislikedMovies() { + var dislikedMovies: [Movie] = [] + dislikedMovies.append(Movie(title: "Avengers: Endgame", year: "2019", genre: "Action", poster: UIImage(named: "avengers-endgame")!)) + dislikedMovies.append(Movie(title: "Joker", year: "2019", genre: "Drama", poster: UIImage(named: "joker")!)) + dislikedMovies.append(Movie(title: "Once Upon a Time in Hollywood", year: "2019", genre: "Comedy", poster: UIImage(named: "once-upon-a-time-in-hollywood")!)) + dislikedMovies.append(Movie(title: "Us", year: "2019", genre: "Horror", poster: UIImage(named: "us")!)) + dislikedMovies.append(Movie(title: "Alita: Battle Angel", year: "2019", genre: "Sci-Fi", poster: UIImage(named: "alita-battle-angel")!)) + dislikedMovies.append(Movie(title: "Aladdin", year: "2019", genre: "Romance", poster: UIImage(named: "aladdin")!)) + } + + func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? { + let movie = matchedMovies[indexPath.row] + let hideAction = UITableViewRowAction(style: .normal, title: "Hide") { (action, indexPath) in + movie.isHidden = true + self.matchedMoviesTableView.reloadData() + } + let blockAction = UITableViewRowAction(style: .normal, title: "Block") { (action, indexPath) in + movie.isBlocked = true + self.matchedMoviesTableView.reloadData() + } + let reportAction = UITableViewRowAction(style: .normal, title: "Report") { (action, indexPath) in + movie.isReported = true + self.matchedMoviesTableView.reloadData() + } + hideAction.backgroundColor = UIColor.lightGray + blockAction.backgroundColor = UIColor.red + reportAction.backgroundColor = UIColor.orange + return [hideAction, blockAction, reportAction] + } + + func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + let movie = matchedMovies[indexPath.row] + if movie.isHidden || movie.isBlocked || movie.isReported { + return false + } + return true + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let movie = matchedMovies[indexPath.row] + if movie.isHidden || movie.isBlocked || movie.isReported { + return + } + performSegue(withIdentifier: "MatchedMovieDetailSegue", sender: self) + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 150 + } + + private func fetchSavedMovies() { + var savedMovies: [Movie] = [] + savedMovies.append(Movie(title: "Avengers: Endgame", year: "2019", genre: "Action", poster: UIImage(named: "avengers-endgame")!)) + savedMovies.append(Movie(title: "Joker", year: "2019", genre: "Drama", poster: UIImage(named: "joker")!)) + savedMovies.append(Movie(title: "Once Upon a Time in Hollywood", year: "2019", genre: "Comedy", poster: UIImage(named: "once-upon-a-time-in-hollywood")!)) + savedMovies.append(Movie(title: "Us", year: "2019", genre: "Horror", poster: UIImage(named: "us")!)) + savedMovies.append(Movie(title: "Alita: Battle Angel", year: "2019", genre: "Sci-Fi", poster: UIImage(named: "alita-battle-angel")!)) + savedMovies.append(Movie(title: "Aladdin", year: "2019", genre: "Romance", poster: UIImage(named: "aladdin")!)) + } + + private func fetchUnsavedMovies() { + var unsavedMovies: [Movie] = [] + unsavedMovies.append(Movie(title: "Avengers: Endgame", year: "2019", genre: "Action", poster: UIImage(named: "avengers-endgame")!)) + unsavedMovies.append(Movie(title: "Joker", year: "2019", genre: "Drama", poster: UIImage(named: "joker")!)) + unsavedMovies.append(Movie(title: "Once Upon a Time in Hollywood", year: "2019", genre: "Comedy", poster: UIImage(named: "once-upon-a-time-in-hollywood")!)) + unsavedMovies.append(Movie(title: "Us", year: "2019", genre: "Horror", poster: UIImage(named: "us")!)) + unsavedMovies.append(Movie(title: "Alita: Battle Angel", year: "2019", genre: "Sci-Fi", poster: UIImage(named: "alita-battle-angel")!)) + unsavedMovies.append(Movie(title: "Aladdin", year: "2019", genre: "Romance", poster: UIImage(named: "aladdin")!)) + } + + private func fetchWatchedMovies() { + var watchedMovies: [Movie] = [] + watchedMovies.append(Movie(title: "Avengers: Endgame", year: "2019", genre: "Action", poster: UIImage(named: "avengers-endgame")!)) + watchedMovies.append(Movie(title: "Joker", year: "2019", genre: "Drama", poster: UIImage(named: "joker")!)) + watchedMovies.append(Movie(title: "Once Upon a Time in Hollywood", year: "2019", genre: "Comedy", poster: UIImage(named: "once-upon-a-time-in-hollywood")!)) + watchedMovies.append(Movie(title: "Us", year: "2019", genre: "Horror", poster: UIImage(named: "us")!)) + watchedMovies.append(Movie(title: "Alita: Battle Angel", year: "2019", genre: "Sci-Fi", poster: UIImage(named: "alita-battle-angel")!)) + watchedMovies.append(Movie(title: "Aladdin", year: "2019", genre: "Romance", poster: UIImage(named: "aladdin")!)) + } + + private func fetchUnwatchedMovies() { + var unwatchedMovies: [Movie] = [] + unwatchedMovies.append(Movie(title: "Avengers: Endgame", year: "2019", genre: "Action", poster: UIImage(named: "avengers-endgame")!)) + unwatchedMovies.append(Movie(title: "Joker", year: "2019", genre: "Drama", poster: UIImage(named: "joker")!)) + unwatchedMovies.append(Movie(title: "Once Upon a Time in Hollywood", year: "2019", genre: "Comedy", poster: UIImage(named: "once-upon-a-time-in-hollywood")!)) + unwatchedMovies.append(Movie(title: "Us", year: "2019", genre: "Horror", poster: UIImage(named: "us")!)) + unwatchedMovies.append(Movie(title: "Alita: Battle Angel", year: "2019", genre: "Sci-Fi", poster: UIImage(named: "alita-battle-angel")!)) + unwatchedMovies.append(Movie(title: "Aladdin", year: "2019", genre: "Romance", poster: UIImage(named: "aladdin")!)) + } + + private func fetchHiddenMovies() { + var hiddenMovies: [Movie] = [] + hiddenMovies.append(Movie(title: "Avengers: Endgame", year: "2019", genre: "Action", poster: UIImage(named: "avengers-endgame")!, isHidden: true)) + hiddenMovies.append(Movie(title: "Joker", year: "2019", genre: "Drama", poster: UIImage(named: "joker")!, isHidden: true)) + hiddenMovies.append(Movie(title: "Once Upon a Time in Hollywood", year: "2019", genre: "Comedy", poster: UIImage(named: "once-upon-a-time-in-hollywood")!, isHidden: true)) + hiddenMovies.append(Movie(title: "Us", year: "2019", genre: "Horror", poster: UIImage(named: "us")!, isHidden: true)) + hiddenMovies.append(Movie(title: "Alita: Battle Angel", year: "2019", genre: "Sci-Fi", poster: UIImage(named: "alita-battle-angel")!, isHidden: true)) + hiddenMovies.append(Movie(title: "Aladdin", year: "2019", genre: "Romance", poster: UIImage(named: "aladdin")!, isHidden: true)) + } + + private func fetchUnhiddenMovies() { + var unhiddenMovies: [Movie] = [] + unhiddenMovies.append(Movie(title: "Avengers: Endgame", year: "2019", genre: "Action", poster: UIImage(named: "avengers-endgame")!, isHidden: false)) + unhiddenMovies.append(Movie(title: "Joker", year: "2019", genre: "Drama", poster: UIImage(named: "joker")!, isHidden: false)) + unhiddenMovies.append(Movie(title: "Once Upon a Time in Hollywood", year: "2019", genre: "Comedy", poster: UIImage(named: "once-upon-a-time-in-hollywood")!, isHidden: false)) + unhiddenMovies.append(Movie(title: "Us", year: "2019", genre: "Horror", poster: UIImage(named: "us")!, isHidden: false)) + unhiddenMovies.append(Movie(title: "Alita: Battle Angel", year: "2019", genre: "Sci-Fi", poster: UIImage(named: "alita-battle-angel")!, isHidden: false)) + unhiddenMovies.append(Movie(title: "Aladdin", year: "2019", genre: "Romance", poster: UIImage(named: "aladdin")!, isHidden: false)) + } + + private func fetchBlockedMovies() { + var blockedMovies: [Movie] = [] + blockedMovies.append(Movie(title: "Avengers: Endgame", year: "2019", genre: "Action", poster: UIImage(named: "avengers-endgame")!, isBlocked: true)) + blockedMovies.append(Movie(title: "Joker", year: "2019", genre: "Drama", poster: UIImage(named: "joker")!, isBlocked: true)) + blockedMovies.append(Movie(title: "Once Upon a Time in Hollywood", year: "2019", genre: "Comedy", poster: UIImage(named: "once-upon-a-time-in-hollywood")!, isBlocked: true)) + blockedMovies.append(Movie(title: "Us", year: "2019", genre: "Horror", poster: UIImage(named: "us")!, isBlocked: true)) + blockedMovies.append(Movie(title: "Alita: Battle Angel", year: "2019", genre: "Sci-Fi", poster: UIImage(named: "alita-battle-angel")!, isBlocked: true)) + blockedMovies.append(Movie(title: "Aladdin", year: "2019", genre: "Romance", poster: UIImage(named: "aladdin")!, isBlocked: true)) + } + + private func fetchUnblockedMovies() { + var unblockedMovies: [Movie] = [] + unblockedMovies.append(Movie(title: "Avengers: Endgame", year: "2019", genre: "Action", poster: UIImage(named: "avengers-endgame")!, isBlocked: false)) + unblockedMovies.append(Movie(title: "Joker", year: "2019", genre: "Drama", poster: UIImage(named: "joker")!, isBlocked: false)) + unblockedMovies.append(Movie(title: "Once Upon a Time in Hollywood", year: "2019", genre: "Comedy", poster: UIImage(named: "once-upon-a-time-in-hollywood")!, isBlocked: false)) + unblockedMovies.append(Movie(title: "Us", year: "2019", genre: "Horror", poster: UIImage(named: "us")!, isBlocked: false)) + unblockedMovies.append(Movie(title: "Alita: Battle Angel", year: "2019", genre: "Sci-Fi", poster: UIImage(named: "alita-battle-angel")!, isBlocked: false)) + unblockedMovies.append(Movie(title: "Aladdin", year: "2019", genre: "Romance", poster: UIImage(named: "aladdin")!, isBlocked: false)) + } + + private func fetchReportedMovies() { + var reportedMovies: [Movie] = [] + reportedMovies.append(Movie(title: "Avengers: Endgame", year: "2019", genre: "Action", poster: UIImage(named: "avengers-endgame")!, isReported: true)) + reportedMovies.append(Movie(title: "Joker", year: "2019", genre: "Drama", poster: UIImage(named: "joker")!, isReported: true)) + reportedMovies.append(Movie(title: "Once Upon a Time in Hollywood", year: "2019", genre: "Comedy", poster: UIImage(named: "once-upon-a-time-in-hollywood")!, isReported: true)) + reportedMovies.append(Movie(title: "Us", year: "2019", genre: "Horror", poster: UIImage(named: "us")!, isReported: true)) + reportedMovies.append(Movie(title: "Alita: Battle Angel", year: "2019", genre: "Sci-Fi", poster: UIImage(named: "alita-battle-angel")!, isReported: true)) + reportedMovies.append(Movie(title: "Aladdin", year: "2019", genre: "Romance", poster: UIImage(named: "aladdin")!, isReported: true)) + } + + private func fetchUnreportedMovies() { + var unreportedMovies: [Movie] = [] + unreportedMovies.append(Movie(title: "Avengers: Endgame", year: "2019", genre: "Action", poster: UIImage(named: "avengers-endgame")!, isReported: false)) + unreportedMovies.append(Movie(title: "Joker", year: "2019", genre: "Drama", poster: UIImage(named: "joker")!, isReported: false)) + unreportedMovies.append(Movie(title: "Once Upon a Time in Hollywood", year: "2019", genre: "Comedy", poster: UIImage(named: "once-upon-a-time-in-hollywood")!, isReported: false)) + unreportedMovies.append(Movie(title: "Us", year: "2019", genre: "Horror", poster: UIImage(named: "us")!, isReported: false)) + unreportedMovies.append(Movie(title: "Alita: Battle Angel", year: "2019", genre: "Sci-Fi", poster: UIImage(named: "alita-battle-angel")!, isReported: false)) + unreportedMovies.append(Movie(title: "Aladdin", year: "2019", genre: "Romance", poster: UIImage(named: "aladdin")!, isReported: false)) + } + + func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? { + let movie = matchedMovies[indexPath.row] + let hideAction = UITableViewRowAction(style: .normal, title: "Hide") { (action, indexPath) in + movie.isHidden = true + self.matchedMoviesTableView.reloadData() + } + let blockAction = UITableViewRowAction(style: .normal, title: "Block") { (action, indexPath) in + movie.isBlocked = true + self.matchedMoviesTableView.reloadData() + } + let reportAction = UITableViewRowAction(style: .normal, title: "Report") { (action, indexPath) in + movie.isReported = true + self.matchedMoviesTableView.reloadData() + } + hideAction.backgroundColor = UIColor.lightGray + blockAction.backgroundColor = UIColor.red + reportAction.backgroundColor = UIColor.orange + return [hideAction, blockAction, reportAction] + } + +} + +struct Movie { + var title: String + var year: String + var genre: String + var poster: UIImage + var isLiked: Bool = false + var isDisliked: Bool = false + var isWatched: Bool = false + var isUnwatched: Bool = false + var isSaved: Bool = false + var isUnsaved: Bool = false + var isHidden: Bool = false + var isUnhidden: Bool = false + var isBlocked: Bool = false + var isUnblocked: Bool = false + var isReported: Bool = false + var isUnreported: Bool = false +} diff --git a/MovieVerse-Mobile/www/swift/MovieSearchView.swift b/MovieVerse-Mobile/www/swift/MovieSearchView.swift new file mode 100644 index 00000000..d656acbd --- /dev/null +++ b/MovieVerse-Mobile/www/swift/MovieSearchView.swift @@ -0,0 +1,124 @@ +import SwiftUI + +struct MovieSearchView: View { + @ObservedObject var viewModel: MovieFetcher + @State private var searchText = "" + + init(viewModel: MovieFetcher) { + self.viewModel = viewModel + } + + var body: some View { + NavigationView { + List { + TextField("Search movies...", text: $searchText, onCommit: { + viewModel.searchMovies(query: searchText) + }) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .padding() + + ForEach(viewModel.movies) { movie in + NavigationLink(destination: MovieDetailsView(movieId: movie.id)) { + Text(movie.title) + } + } + } + .navigationBarTitle("Search Movies") + } + } + + var view = MovieSearchView(viewModel: MovieFetcher()) + + var body: some View { + view + } + +} + +struct MovieSearchView_Previews: PreviewProvider { + static var previews: some View { + MovieSearchView(viewModel: MovieFetcher()) + } +} + +struct MovieDetailsView: View { + @ObservedObject var viewModel: MovieDetailsViewModel + let movieId: Int + + init(movieId: Int) { + self.movieId = movieId + self.viewModel = MovieDetailsViewModel(movieId: movieId) + } + + var body: some View { + ScrollView { + VStack { + if let movie = viewModel.movieDetails { + MoviePosterImage(url: movie.posterURL) + .frame(width: 200, height: 300) + .cornerRadius(8) + + Text(movie.title) + .font(.title) + .fontWeight(.bold) + .padding(.top, 10) + + Text("Rating: \(movie.voteAverage, specifier: "%.1f") / 10") + .font(.subheadline) + .padding(.vertical, 4) + + Text(movie.overview) + .font(.body) + .padding() + } else { + Text("Loading movie details...") + .font(.headline) + .foregroundColor(.gray) + } + } + } + .onAppear { + viewModel.fetchMovieDetails() + } + } +} + +struct MoviePosterImage: View { + let url: URL + + @State private var imageData: Data? + + var body: some View { + if let imageData = imageData, let uiImage = UIImage(data: imageData) { + Image(uiImage: uiImage) + .resizable() + } + else { + Rectangle() + .foregroundColor(.gray) + .onAppear { + fetchImageData() + } + } + } + + private func fetchImageData() { + URLSession.shared.dataTask(with: url) { data, response, error in + guard let data = data else { + print("No data in response: \(error?.localizedDescription ?? "Unknown error")") + return + } + + DispatchQueue.main.async { + self.imageData = data + } + } + .resume() + } +} + +struct MovieDetailsView_Previews: PreviewProvider { + static var previews: some View { + MovieDetailsView(movieId: 1) + } +} diff --git a/MovieVerse-Mobile/www/swift/MovieTimelineViewController.swift b/MovieVerse-Mobile/www/swift/MovieTimelineViewController.swift new file mode 100644 index 00000000..b9f89e33 --- /dev/null +++ b/MovieVerse-Mobile/www/swift/MovieTimelineViewController.swift @@ -0,0 +1,203 @@ +import UIKit + +// MARK: - Data Model +struct MovieTimelineSection { + let period: String + let movies: [Movie] +} + +struct Movie { + let title: String + let releaseYear: String + let description: String + let posterURL: String +} + +class MovieTimelineViewController: UIViewController { + + // Sample Data + private let timelineSections: [MovieTimelineSection] = [ + MovieTimelineSection(period: "1920s", movies: [Movie(title: "Movie 1", releaseYear: "1920", description: "Description 1", posterURL: "URL1")]), + MovieTimelineSection(period: "1930s", movies: [Movie(title: "Movie 2", releaseYear: "1930", description: "Description 2", posterURL: "URL2")]), + MovieTimelineSection(period: "1940s", movies: [Movie(title: "Movie 3", releaseYear: "1940", description: "Description 3", posterURL: "URL3")]), + MovieTimelineSection(period: "1950s", movies: [Movie(title: "Movie 4", releaseYear: "1950", description: "Description 4", posterURL: "URL4")]), + MovieTimelineSection(period: "1960s", movies: [Movie(title: "Movie 5", releaseYear: "1960", description: "Description 5", posterURL: "URL5")]), + MovieTimelineSection(period: "1970s", movies: [Movie(title: "Movie 6", releaseYear: "1970", description: "Description 6", posterURL: "URL6")]), + MovieTimelineSection(period: "1980s", movies: [Movie(title: "Movie 7", releaseYear: "1980", description: "Description 7", posterURL: "URL7")]), + MovieTimelineSection(period: "1990s", movies: [Movie(title: "Movie 8", releaseYear: "1990", description: "Description 8", posterURL: "URL8")]), + MovieTimelineSection(period: "2000s", movies: [Movie(title: "Movie 9", releaseYear: "2000", description: "Description 9", posterURL: "URL9")]), + MovieTimelineSection(period: "2010s", movies: [Movie(title: "Movie 10", releaseYear: "2010", description: "Description 10", posterURL: "URL10")]), + MovieTimelineSection(period: "2020s", movies: [Movie(title: "Movie 11", releaseYear: "2020", description: "Description 11", posterURL: "URL11")]) + ] + + // UI Components + private lazy var tableView: UITableView = { + let table = UITableView() + table.delegate = self + table.dataSource = self + table.register(MovieTimelineCell.self, forCellReuseIdentifier: MovieTimelineCell.identifier) + return table + }() + + // MARK: - Lifecycle + override func viewDidLoad() { + super.viewDidLoad() + setupUI() + } + + // MARK: - Setup + private func setupUI() { + view.addSubview(tableView) + tableView.frame = view.bounds + title = "Movie Timeline" + } + + // MARK: - Reuse + override func prepareForReuse() { + super.prepareForReuse() + tableView.reloadData() + } +} + +// MARK: - UITableView DataSource & Delegate +extension MovieTimelineViewController: UITableViewDataSource, UITableViewDelegate { + + func numberOfSections(in tableView: UITableView) -> Int { + return timelineSections.count + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return timelineSections[section].movies.count + } + + func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + return timelineSections[section].period + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: MovieTimelineCell.identifier, for: indexPath) as? MovieTimelineCell else { + return UITableViewCell() + } + let movie = timelineSections[indexPath.section].movies[indexPath.row] + cell.configure(with: movie) + return cell + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 120 + } + + func sectionIndexTitles(for tableView: UITableView) -> [String]? { + return timelineSections.map { $0.period } + } + + func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int { + return timelineSections.firstIndex { $0.period == title } ?? 0 + } +} + +// MARK: - Custom TableViewCell +class MovieTimelineCell: UITableViewCell { + + static let identifier = "MovieTimelineCell" + + // UI Elements for the cell + private let titleLabel: UILabel = { + let label = UILabel() + label.font = UIFont.boldSystemFont(ofSize: 18) + return label + }() + + private let yearLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 16) + label.textColor = .gray + return label + }() + + private let descriptionLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 16) + label.numberOfLines = 0 + return label + }() + + private let posterImageView: UIImageView = { + let imageView = UIImageView() + imageView.contentMode = .scaleAspectFit + imageView.clipsToBounds = true + return imageView + }() + + // Initialization + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + setupLayout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // Configure Cell + func configure(with movie: Movie) { + titleLabel.text = movie.title + yearLabel.text = "Release Year: \(movie.releaseYear)" + descriptionLabel.text = movie.description + posterImageView.image = UIImage(named: movie.posterURL) // replace with actual image loading logic + } + + // Layout + private func setupLayout() { + let stackView = UIStackView(arrangedSubviews: [titleLabel, yearLabel, descriptionLabel]) + stackView.axis = .vertical + stackView.spacing = 4 + stackView.translatesAutoresizingMaskIntoConstraints = false + + addSubview(posterImageView) + addSubview(stackView) + + NSLayoutConstraint.activate([ + posterImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10), + posterImageView.topAnchor.constraint(equalTo: topAnchor, constant: 10), + posterImageView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10), + posterImageView.widthAnchor.constraint(equalToConstant: 100), + + stackView.leadingAnchor.constraint(equalTo: posterImageView.trailingAnchor, constant: 10), + stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10), + stackView.topAnchor.constraint(equalTo: topAnchor, constant: 10), + stackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10) + ]) + } + + private func applyConstraints() { + titleLabel.translatesAutoresizingMaskIntoConstraints = false + yearLabel.translatesAutoresizingMaskIntoConstraints = false + descriptionLabel.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + titleLabel.topAnchor.constraint(equalTo: topAnchor, constant: 10), + titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10), + titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10), + + yearLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 5), + yearLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10), + yearLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10), + + descriptionLabel.topAnchor.constraint(equalTo: yearLabel.bottomAnchor, constant: 5), + descriptionLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10), + descriptionLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10), + descriptionLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10) + ]) + } + + // MARK: - Reuse + override func prepareForReuse() { + super.prepareForReuse() + titleLabel.text = nil + yearLabel.text = nil + descriptionLabel.text = nil + posterImageView.image = nil + } + +} diff --git a/MovieVerse-Mobile/www/swift/MovieTriviaViewController.swift b/MovieVerse-Mobile/www/swift/MovieTriviaViewController.swift new file mode 100644 index 00000000..933f8caa --- /dev/null +++ b/MovieVerse-Mobile/www/swift/MovieTriviaViewController.swift @@ -0,0 +1,186 @@ +import UIKit + +// MARK: - Data Model +struct TriviaQuestion { + let question: String + let options: [String] + let correctAnswer: String +} + +class MovieTriviaViewController: UIViewController { + +// Data in JS +// const questionBank = [ +// { question: "What movie won the Academy Award for Best Picture in 2020?", options: ["Joker", "1917", "Parasite"], answer: "Parasite" }, +// { question: "Who directed the movie 'The Godfather'?", options: ["Steven Spielberg", "Francis Ford Coppola", "Martin Scorsese"], answer: "Francis Ford Coppola" }, +// { question: "What was the first feature-length animated movie ever released?", options: ["Snow White and the Seven Dwarfs", "Bambi", "Pinocchio"], answer: "Snow White and the Seven Dwarfs" }, +// { question: "What was the highest-grossing movie of all time before the release of 'Avatar'?", options: ["Titanic", "Star Wars: The Force Awakens", "Avengers: Endgame"], answer: "Titanic" }, +// { question: "Who played the lead role in the movie 'Forrest Gump'?", options: ["Tom Hanks", "Brad Pitt", "Leonardo DiCaprio"], answer: "Tom Hanks" }, +// { question: "What movie won the Academy Award for Best Picture in 2019?", options: ["Bohemian Rhapsody", "Green Book", "Roma"], answer: "Green Book" }, +// { question: "Who played the character of John McClane in the Die Hard movie series?", options: ["Arnold Schwarzenegger", "Sylvester Stallone", "Bruce Willis"], answer: "Bruce Willis" }, +// { question: "What movie is based on a novel by Stephen King and features a character named Jack Torrance?", options: ["Carrie", "The Shining", "Misery"], answer: "The Shining" }, +// { question: "Who directed the movie 'Forrest Gump'?", options: ["Steven Spielberg", "Robert Zemeckis", "Martin Scorsese"], answer: "Robert Zemeckis" }, +// { question: "What is the highest-grossing movie of all time (as of 2021)?", options: ["Avatar", "Avengers: Endgame", "Titanic"], answer: "Avatar" }, +// { question: "What movie features the line 'You can't handle the truth!'?", options: ["The Shawshank Redemption", "A Few Good Men", "Goodfellas"], answer: "A Few Good Men" }, +// { question: "Who played the character of Tony Stark/Iron Man in the Marvel Cinematic Universe?", options: ["Chris Hemsworth", "Mark Ruffalo", "Robert Downey Jr."], answer: "Robert Downey Jr." }, +// { question: "In which movie did Tom Hanks say, 'Houston, we have a problem'?", options: ["Apollo 13", "Cast Away", "The Terminal"], answer: "Apollo 13" }, +// { question: "What is the name of the hobbit played by Elijah Wood in the Lord of the Rings movies?", options: ["Frodo", "Sam", "Merry"], answer: "Frodo" }, +// { question: "What is the name of the kingdom where the 2013 animated movie 'Frozen' is set?", options: ["Arendelle", "Genovia", "DunBroch"], answer: "Arendelle" }, +// { question: "Which 1997 science fiction movie stars Will Smith and Tommy Lee Jones?", options: ["Independence Day", "Men in Black", "Wild Wild West"], answer: "Men in Black" }, +// { question: "Which movie features Bruce Willis as a child psychologist?", options: ["Die Hard", "The Sixth Sense", "Unbreakable"], answer: "The Sixth Sense" }, +// { question: "In 'The Matrix', does Neo take the blue pill or the red pill?", options: ["Blue", "Red", "Green"], answer: "Red" }, +// { question: "Which actress played Katniss Everdeen in 'The Hunger Games' movies?", options: ["Jennifer Lawrence", "Emma Watson", "Shailene Woodley"], answer: "Jennifer Lawrence" }, +// { question: "Who directed 'Jurassic Park'?", options: ["James Cameron", "Steven Spielberg", "George Lucas"], answer: "Steven Spielberg" }, +// { question: "What 1980s movie was the highest grossing film of the decade?", options: ["E.T. the Extra-Terrestrial", "Star Wars: Episode V", "Back to the Future"], answer: "E.T. the Extra-Terrestrial" }, +// { question: "Which movie features the song 'My Heart Will Go On'?", options: ["The Bodyguard", "Titanic", "Romeo + Juliet"], answer: "Titanic" }, +// { question: "What was the first Pixar movie?", options: ["Toy Story", "A Bug's Life", "Monsters, Inc."], answer: "Toy Story" }, +// { question: "Who played Wolverine in the X-Men movies?", options: ["Hugh Jackman", "Liam Hemsworth", "Chris Evans"], answer: "Hugh Jackman" }, +// { question: "Which film did NOT win the Academy Award for Best Picture?", options: ["The Shawshank Redemption", "The Godfather", "Forrest Gump"], answer: "The Shawshank Redemption" }, +// { question: "What is Indiana Jones' real first name?", options: ["Henry", "John", "Walter"], answer: "Henry" }, +// { question: "In 'The Wizard of Oz', what did the Scarecrow want from the wizard?", options: ["Heart", "Brain", "Courage"], answer: "Brain" }, +// { question: "Who is the only actor to receive an Oscar nomination for acting in a Lord of the Rings movie?", options: ["Viggo Mortensen", "Ian McKellen", "Elijah Wood"], answer: "Ian McKellen" }, +// { question: "Which movie features an iconic dance scene between Uma Thurman and John Travolta?", options: ["Pulp Fiction", "Kill Bill", "Saturday Night Fever"], answer: "Pulp Fiction" }, +// { question: "What is the highest-grossing R-rated movie of all time?", options: ["Deadpool", "Joker", "The Matrix"], answer: "Joker" }, +// { question: "Which Alfred Hitchcock movie is notorious for its shower scene?", options: ["Vertigo", "Psycho", "Rear Window"], answer: "Psycho" }, +// { question: "What is Darth Vader's real name?", options: ["Anakin Skywalker", "Luke Skywalker", "Obi-Wan Kenobi"], answer: "Anakin Skywalker" }, +// { question: "Who directed 'Schindler's List'?", options: ["Martin Scorsese", "Steven Spielberg", "Ridley Scott"], answer: "Steven Spielberg" }, +// { question: "In which movie does Tom Cruise perform his own stunts climbing the Burj Khalifa?", options: ["Mission: Impossible - Rogue Nation", "Mission: Impossible - Ghost Protocol", "Edge of Tomorrow"], answer: "Mission: Impossible - Ghost Protocol" }, +// { question: "What is the name of the fictional African country where 'Black Panther' is set?", options: ["Wakanda", "Genovia", "Zamunda"], answer: "Wakanda" }, +// { question: "Who directed 'Inception' and 'Interstellar'?", options: ["Christopher Nolan", "James Cameron", "Steven Spielberg"], answer: "Christopher Nolan" }, +// { question: "In 'The Hunger Games', what district do Katniss and Peeta represent?", options: ["District 12", "District 9", "District 7"], answer: "District 12" }, +// { question: "Which movie features a character named Tyler Durden?", options: ["Fight Club", "Gone Girl", "Seven"], answer: "Fight Club" }, +// { question: "What is the name of the island in 'Jurassic Park'?", options: ["Isla Nublar", "Isla Sorna", "Skull Island"], answer: "Isla Nublar" }, +// { question: "Who played the Joker in 'The Dark Knight'?", options: ["Heath Ledger", "Joaquin Phoenix", "Jared Leto"], answer: "Heath Ledger" }, +// { question: "In which movie is the fictional company Initech featured?", options: ["Office Space", "The Social Network", "Wall Street"], answer: "Office Space" }, +// { question: "What year was the first 'Harry Potter' movie released?", options: ["2001", "2003", "1999"], answer: "2001" }, +// { question: "What fictional country is 'Wonder Woman' from?", options: ["Themyscira", "Asgard", "Genovia"], answer: "Themyscira" }, +// { question: "Which movie is known for the quote 'Here's looking at you, kid'?", options: ["Casablanca", "Gone with the Wind", "The Maltese Falcon"], answer: "Casablanca" }, +// { question: "In 'The Lion King', what is Simba's mother's name?", options: ["Nala", "Sarabi", "Shenzi"], answer: "Sarabi" }, +// { question: "Who directed 'Avengers: Endgame'?", options: ["The Russo Brothers", "Joss Whedon", "Jon Favreau"], answer: "The Russo Brothers" }, +// { question: "What is the name of the kingdom in 'Tangled'?", options: ["Corona", "Far Far Away", "Arendelle"], answer: "Corona" }, +// { question: "Which film features a famous dance scene with Uma Thurman and John Travolta?", options: ["Pulp Fiction", "Saturday Night Fever", "Kill Bill"], answer: "Pulp Fiction" }, +// { question: "Who played Jack Dawson in 'Titanic'?", options: ["Leonardo DiCaprio", "Brad Pitt", "Johnny Depp"], answer: "Leonardo DiCaprio" }, +// { question: "What is the highest-grossing film of all time?", options: ["Avengers: Endgame", "Avatar", "Titanic"], answer: "Avatar" }, +// { question: "In which movie does the character Neo appear?", options: ["The Matrix", "John Wick", "Speed"], answer: "The Matrix" }, +// { question: "What is the real name of the Black Panther in the Marvel Cinematic Universe?", options: ["T'Challa", "M'Baku", "N'Jadaka"], answer: "T'Challa" }, +// { question: "Who directed 'Mad Max: Fury Road'?", options: ["George Miller", "Ridley Scott", "Peter Jackson"], answer: "George Miller" }, +// { question: "What animated film features a character named 'Hiccup'?", options: ["Brave", "How to Train Your Dragon", "Shrek"], answer: "How to Train Your Dragon" }, +// { question: "In which film is the fictional mineral 'Unobtainium' sought after?", options: ["Avatar", "The Core", "Transformers"], answer: "Avatar" }, +// ]; + + // Trivia Data + private var questions: [TriviaQuestion] = [ + TriviaQuestion(question: "What movie won the Academy Award for Best Picture in 2020?", options: ["Joker", "1917", "Parasite"], correctAnswer: "Parasite"), + TriviaQuestion(question: "Who directed the movie 'The Godfather'?", options: ["Steven Spielberg", "Francis Ford Coppola", "Martin Scorsese"], correctAnswer: "Francis Ford Coppola"), + TriviaQuestion(question: "What was the first feature-length animated movie ever released?", options: ["Snow White and the Seven Dwarfs", "Bambi", "Pinocchio"], correctAnswer: "Snow White and the Seven Dwarfs"), + TriviaQuestion(question: "What was the highest-grossing movie of all time before the release of 'Avatar'?", options: ["Titanic", "Star Wars: The Force Awakens", "Avengers: Endgame"], correctAnswer: "Titanic"), + TriviaQuestion(question: "Who played the lead role in the movie 'Forrest Gump'?", options: ["Tom Hanks", "Brad Pitt", "Leonardo DiCaprio"], correctAnswer: "Tom Hanks"), + TriviaQuestion(question: "What movie won the Academy Award for Best Picture in 2019?", options: ["Bohemian Rhapsody", "Green Book", "Roma"], correctAnswer: "Green Book"), + TriviaQuestion(question: "Who played the character of John McClane in the Die Hard movie series?", options: ["Arnold Schwarzenegger", "Sylvester Stallone", "Bruce Willis"], correctAnswer: "Bruce Willis"), + TriviaQuestion(question: "What movie is based on a novel by Stephen King and features a character named Jack Torrance?", options: ["Carrie", "The Shining", "Misery"], correctAnswer: "The Shining"), + TriviaQuestion(question: "Who directed the movie 'Forrest Gump'?", options: ["Steven Spielberg", "Robert Zemeckis", "Martin Scorsese"], correctAnswer: "Robert Zemeckis"), + TriviaQuestion(question: "What is the highest-grossing movie of all time (as of 2021)?", options: ["Avatar", "Avengers: Endgame", "Titanic"], correctAnswer: "Avatar"), + ] + + private var currentQuestionIndex = 0 + private var score = 0 + + // UI Components + private let questionLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 20) + label.numberOfLines = 0 + label.textAlignment = .center + return label + }() + + private var optionButtons: [UIButton] = [] + + // MARK: - Lifecycle + override func viewDidLoad() { + super.viewDidLoad() + setupUI() + loadQuestion() + } + + // MARK: - Setup + private func setupUI() { + view.backgroundColor = .white + view.addSubview(questionLabel) + + setupOptionButtons() + layoutUI() + title = "Movie Trivia" + } + + private func setupOptionButtons() { + for _ in 1...4 { + let button = UIButton(type: .system) + button.titleLabel?.font = UIFont.systemFont(ofSize: 18) + button.addTarget(self, action: #selector(optionButtonTapped), for: .touchUpInside) + optionButtons.append(button) + view.addSubview(button) + } + } + + private func layoutUI() { + questionLabel.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + questionLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20), + questionLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), + questionLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20) + ]) + + var previousButton: UIButton? + for button in optionButtons { + button.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + button.topAnchor.constraint(equalTo: previousButton?.bottomAnchor ?? questionLabel.bottomAnchor, constant: 20), + button.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), + button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20) + ]) + previousButton = button + } + } + + // MARK: - Actions + @objc private func optionButtonTapped(_ sender: UIButton) { + guard let answer = sender.titleLabel?.text else { return } + checkAnswer(answer) + } + + // MARK: - Trivia Logic + private func loadQuestion() { + guard currentQuestionIndex < questions.count else { + // Show results + showResults() + return + } + + let currentQuestion = questions[currentQuestionIndex] + questionLabel.text = currentQuestion.question + + for (index, button) in optionButtons.enumerated() { + button.setTitle(currentQuestion.options[index], for: .normal) + } + } + + private func checkAnswer(_ answer: String) { + let currentQuestion = questions[currentQuestionIndex] + if answer == currentQuestion.correctAnswer { + score += 1 + } + + currentQuestionIndex += 1 + loadQuestion() + } + + private func showResults() { + let alertController = UIAlertController(title: "Trivia Complete", message: "Your score: \(score)/\(questions.count)", preferredStyle: .alert) + let action = UIAlertAction(title: "OK", style: .default) { _ in + self.navigationController?.popViewController(animated: true) + } + alertController.addAction(action) + present(alertController, animated: true) + } +} diff --git a/MovieVerse-Mobile/www/swift/MoviesViewController.swift b/MovieVerse-Mobile/www/swift/MoviesViewController.swift new file mode 100644 index 00000000..ea4bb2d2 --- /dev/null +++ b/MovieVerse-Mobile/www/swift/MoviesViewController.swift @@ -0,0 +1,222 @@ +import UIKit + +class MoviesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { + + @IBOutlet weak var tableView: UITableView! + + var movies = [Movie]() + + override func viewDidLoad() { + super.viewDidLoad() + tableView.delegate = self + tableView.dataSource = self + + fetchMovies() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + fetchMovies() + } + + private func saveFavorites() { + let encoder = JSONEncoder() + if let encoded = try? encoder.encode(movies) { + UserDefaults.standard.set(encoded, forKey: "Favorites") + } + } + + private func loadFavorites() { + if let savedMovies = UserDefaults.standard.object(forKey: "Favorites") as? Data { + let decoder = JSONDecoder() + if let loadedMovies = try? decoder.decode([Movie].self, from: savedMovies) { + movies = loadedMovies + tableView.reloadData() + } + } + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if segue.identifier == "showDetail", + let destinationVC = segue.destination as? MovieDetailViewController, + let movie = sender as? Movie { + destinationVC.movie = movie + } + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let movie = movies[indexPath.row] + performSegue(withIdentifier: "showDetail", sender: movie) + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return movies.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + + let cell = tableView.dequeueReusableCell(withIdentifier: "MovieCell", for: indexPath) + let movie = movies[indexPath.row] + cell.textLabel?.text = movie.title + return cell + + } + + func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { + if editingStyle == .delete { + movies.remove(at: indexPath.row) + tableView.deleteRows(at: [indexPath], with: .fade) + saveFavorites() + } + } + + func fetchMovies() { + guard let url = URL(string: "https://api.example.com/movies") else { + print("Invalid URL") + return + } + let task = URLSession.shared.dataTask(with: url) { data, response, error in + guard let data = data, error == nil else { + print("Network error: \(error?.localizedDescription ?? "Unknown error")") + return + } + do { + let movies = try JSONDecoder().decode([Movie].self, from: data) + DispatchQueue.main.async { + self.movies = movies + self.tableView.reloadData() + } + } + catch { + print("JSON error: \(error.localizedDescription)") + } + } + task.resume() + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return movies.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "MovieCell", for: indexPath) + let movie = movies[indexPath.row] + cell.textLabel?.text = movie.title + return cell + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let movie = movies[indexPath.row] + performSegue(withIdentifier: "showDetail", sender: movie) + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if segue.identifier == "showDetail", + let destinationVC = segue.destination as? MovieDetailViewController, + let movie = sender as? Movie { + destinationVC.movie = movie + } + } + +} + +struct Movie: Codable { + let title: String + let overview: String +} + +struct MovieDetailViewController: UIViewController { + var movie: Movie? + + override func viewDidLoad() { + super.viewDidLoad() + guard let movie = movie else { + print("No movie") + return + } + print(movie.title) + } +} + +struct FavoritesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { + + @IBOutlet weak var tableView: UITableView! + + var favoriteMovies: [Movie] = [] + var movie: Movie? + + override func viewDidLoad() { + super.viewDidLoad() + tableView.delegate = self + tableView.dataSource = self + + loadFavorites() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + loadFavorites() + } + + private func loadFavorites() { + if let savedMovies = UserDefaults.standard.object(forKey: "Favorites") as? Data { + let decoder = JSONDecoder() + if let loadedMovies = try? decoder.decode([Movie].self, from: savedMovies) { + favoriteMovies = loadedMovies + tableView.reloadData() + } + } + } + + private func saveFavorites() { + let encoder = JSONEncoder() + if let encoded = try? encoder.encode(favoriteMovies) { + UserDefaults.standard.set(encoded, forKey: "Favorites") + } + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return favoriteMovies.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "FavoriteMovieCell", for: indexPath) + let movie = favoriteMovies[indexPath.row] + cell.textLabel?.text = movie.title + return cell + } + + func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { + if editingStyle == .delete { + favoriteMovies.remove(at: indexPath.row) + tableView.deleteRows(at: [indexPath], with: .fade) + saveFavorites() + } + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let movie = favoriteMovies[indexPath.row] + performSegue(withIdentifier: "showDetail", sender: movie) + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if segue.identifier == "showDetail", + let destinationVC = segue.destination as? MovieDetailViewController, + let movie = sender as? Movie { + destinationVC.movie = movie + } + } +} + +struct MovieImageViewController: UIViewController { + var movie: Movie? + + override func viewDidLoad() { + super.viewDidLoad() + guard let movie = movie else { + print("No movie") + return + } + print(movie.title) + } +} diff --git a/MovieVerse-Mobile/www/swift/NetworkManager.swift b/MovieVerse-Mobile/www/swift/NetworkManager.swift new file mode 100644 index 00000000..208dab8c --- /dev/null +++ b/MovieVerse-Mobile/www/swift/NetworkManager.swift @@ -0,0 +1,39 @@ +import Foundation + +class NetworkManager { + static let shared = NetworkManager() + + private init() {} + + func fetchData(from urlString: String, completion: @escaping (Result) -> Void) { + guard let url = URL(string: urlString) else { + completion(.failure(NetworkError.invalidURL)) + return + } + + URLSession.shared.dataTask(with: url) { data, response, error in + if let error = error { + completion(.failure(error)) + return + } + + guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { + completion(.failure(NetworkError.invalidResponse)) + return + } + + guard let data = data else { + completion(.failure(NetworkError.noData)) + return + } + + completion(.success(data)) + }.resume() + } + + enum NetworkError: Error { + case invalidURL + case invalidResponse + case noData + } +} diff --git a/MovieVerse-Mobile/www/swift/OfflineManager.swift b/MovieVerse-Mobile/www/swift/OfflineManager.swift new file mode 100644 index 00000000..2c97d7c5 --- /dev/null +++ b/MovieVerse-Mobile/www/swift/OfflineManager.swift @@ -0,0 +1,42 @@ +import Foundation +import SystemConfiguration + +class OfflineManager { + static let shared = OfflineManager() + + private init() {} + + func saveData(_ data: [String: Any], forKey key: String) { + UserDefaults.standard.set(data, forKey: key) + UserDefaults.standard.synchronize() + } + + func loadData(forKey key: String) -> [String: Any]? { + return UserDefaults.standard.dictionary(forKey: key) + } + + func isNetworkAvailable() -> Bool { + var zeroAddress = sockaddr_in() + zeroAddress.sin_len = UInt8(MemoryLayout.size) + zeroAddress.sin_family = sa_family_t(AF_INET) + + guard let defaultRouteReachability = withUnsafePointer(to: &zeroAddress, { + $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { + SCNetworkReachabilityCreateWithAddress(nil, $0) + } + }) + else { + return false + } + + var flags: SCNetworkReachabilityFlags = [] + if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) { + return false + } + + let isReachable = flags.contains(.reachable) + let needsConnection = flags.contains(.connectionRequired) + + return (isReachable && !needsConnection) + } +} \ No newline at end of file diff --git a/backend_microservices/backend_django/__pycache__/__init__.cpython-311.pyc b/backend_microservices/backend_django/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 00000000..492c2fd1 Binary files /dev/null and b/backend_microservices/backend_django/__pycache__/__init__.cpython-311.pyc differ diff --git a/backend_microservices/backend_django/__pycache__/settings.cpython-311.pyc b/backend_microservices/backend_django/__pycache__/settings.cpython-311.pyc new file mode 100644 index 00000000..b1d09563 Binary files /dev/null and b/backend_microservices/backend_django/__pycache__/settings.cpython-311.pyc differ diff --git a/backend_microservices/backend_django/__pycache__/urls.cpython-311.pyc b/backend_microservices/backend_django/__pycache__/urls.cpython-311.pyc new file mode 100644 index 00000000..fad1b69a Binary files /dev/null and b/backend_microservices/backend_django/__pycache__/urls.cpython-311.pyc differ diff --git a/backend_microservices/backend_django/__pycache__/wsgi.cpython-311.pyc b/backend_microservices/backend_django/__pycache__/wsgi.cpython-311.pyc new file mode 100644 index 00000000..9d2c44f5 Binary files /dev/null and b/backend_microservices/backend_django/__pycache__/wsgi.cpython-311.pyc differ