Skip to content

Commit

Permalink
feat(android): implement BridgeFragment for easier embedding using Fr…
Browse files Browse the repository at this point in the history
…agments (#2666)

Co-authored-by: jcesarmobile <jcesarmobile@gmail.com>
  • Loading branch information
mlynch and jcesarmobile authored Apr 3, 2020
1 parent 4fcb725 commit a8d9591
Show file tree
Hide file tree
Showing 12 changed files with 343 additions and 3 deletions.
21 changes: 20 additions & 1 deletion android/capacitor/src/main/java/com/getcapacitor/Bridge.java
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,12 @@ public String getServerBasePath() {
return this.localServer.getBasePath();
}

public void setServerBasePath(String path){
/**
* Tell the local server to load files from the given
* file path instead of the assets path.
* @param path
*/
public void setServerBasePath(String path) {
localServer.hostFiles(path);
webView.post(new Runnable() {
@Override
Expand All @@ -871,6 +876,20 @@ public void run() {
});
}

/**
* Tell the local server to load files from the given
* asset path.
* @param path
*/
public void setServerAssetPath(String path) {
localServer.hostAssets(path);
webView.post(new Runnable() {
@Override
public void run() {
webView.loadUrl(appUrl);
}
});
}

public String getLocalUrl() {
return localUrl;
Expand Down
197 changes: 197 additions & 0 deletions android/capacitor/src/main/java/com/getcapacitor/BridgeFragment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
package com.getcapacitor;

import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.net.Uri;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;

import androidx.fragment.app.Fragment;

import com.getcapacitor.android.R;
import com.getcapacitor.cordova.MockCordovaInterfaceImpl;
import com.getcapacitor.cordova.MockCordovaWebViewImpl;

import org.apache.cordova.ConfigXmlParser;
import org.apache.cordova.CordovaPreferences;
import org.apache.cordova.PluginEntry;
import org.apache.cordova.PluginManager;

import java.util.ArrayList;
import java.util.List;

/**
* A simple {@link Fragment} subclass.
* Activities that contain this fragment must implement the
* {@link BridgeFragment.OnFragmentInteractionListener} interface
* to handle interaction events.
* Use the {@link BridgeFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class BridgeFragment extends Fragment {
private static final String ARG_START_DIR = "startDir";

private String startDir;

private OnFragmentInteractionListener mListener;

private WebView webView;
protected Bridge bridge;
protected MockCordovaInterfaceImpl cordovaInterface;
protected boolean keepRunning = true;
private ArrayList<PluginEntry> pluginEntries;
private PluginManager pluginManager;
private CordovaPreferences preferences;
private MockCordovaWebViewImpl mockWebView;
private int activityDepth = 0;
private String bridgeStartDir;

private String lastActivityPlugin;

private List<Class<? extends Plugin>> initialPlugins = new ArrayList<>();


public BridgeFragment() {
// Required empty public constructor
}

/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param startDir the directory to serve content from
* @return A new instance of fragment BridgeFragment.
*/
public static BridgeFragment newInstance(String startDir) {
BridgeFragment fragment = new BridgeFragment();
Bundle args = new Bundle();
args.putString(ARG_START_DIR, startDir);
fragment.setArguments(args);
return fragment;
}

protected void init(Bundle savedInstanceState) {
loadConfig(this.getActivity().getApplicationContext(), this.getActivity());
}

/**
* Load the WebView and create the Bridge
*/
protected void load(Bundle savedInstanceState) {
Log.d(LogUtils.getCoreTag(), "Starting BridgeActivity");

Bundle args = getArguments();
String startDir = null;

if (args != null) {
startDir = getArguments().getString(ARG_START_DIR);
}

webView = getView().findViewById(R.id.webview);
cordovaInterface = new MockCordovaInterfaceImpl(this.getActivity());
if (savedInstanceState != null) {
cordovaInterface.restoreInstanceState(savedInstanceState);
}

mockWebView = new MockCordovaWebViewImpl(getActivity().getApplicationContext());
mockWebView.init(cordovaInterface, pluginEntries, preferences, webView);

pluginManager = mockWebView.getPluginManager();
cordovaInterface.onCordovaInit(pluginManager);

if (preferences == null) {
preferences = new CordovaPreferences();
}

bridge = new Bridge(this.getActivity(), webView, initialPlugins, cordovaInterface, pluginManager, preferences);

if (startDir != null) {
bridge.setServerAssetPath(startDir);
}

if (savedInstanceState != null) {
bridge.restoreInstanceState(savedInstanceState);
}
this.keepRunning = preferences.getBoolean("KeepRunning", true);
}

public void loadConfig(Context context, Activity activity) {
ConfigXmlParser parser = new ConfigXmlParser();
parser.parse(context);
preferences = parser.getPreferences();
preferences.setPreferencesBundle(activity.getIntent().getExtras());
pluginEntries = parser.getPluginEntries();
}


@Override
public void onInflate(Context context, AttributeSet attrs, Bundle savedInstanceState) {
super.onInflate(context, attrs, savedInstanceState);

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.bridge_fragment);
CharSequence c = a.getString(R.styleable.bridge_fragment_start_dir);

if (c != null) {
String startDir = c.toString();
Bundle args = new Bundle();
args.putString(ARG_START_DIR, startDir);
setArguments(args);
}
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_bridge, container, false);
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
this.init(savedInstanceState);
this.load(savedInstanceState);
}

@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}

@Override
public void onDetach() {
super.onDetach();
mListener = null;
}

/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnFragmentInteractionListener {
void onFragmentInteraction(Uri uri);
}
}
13 changes: 11 additions & 2 deletions android/capacitor/src/main/java/com/getcapacitor/Splash.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import android.animation.Animator;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.PixelFormat;
Expand Down Expand Up @@ -47,7 +48,14 @@ private static void buildViews(Context c) {
String splashResourceName = Config.getString(CONFIG_KEY_PREFIX + "androidSplashResourceName", "splash");

int splashId = c.getResources().getIdentifier(splashResourceName, "drawable", c.getPackageName());
Drawable splash = c.getResources().getDrawable(splashId, c.getTheme());

Drawable splash;
try {
splash = c.getResources().getDrawable(splashId, c.getTheme());
} catch (Resources.NotFoundException ex) {
Log.w(LogUtils.getCoreTag(), "No splash screen found, not displaying");
return;
}

if (splash instanceof Animatable) {
((Animatable) splash).start();
Expand Down Expand Up @@ -257,8 +265,9 @@ public void run() {

try {
wm.addView(splashImage, params);
} catch (IllegalStateException ex) {
} catch (IllegalStateException | IllegalArgumentException ex) {
Log.d(LogUtils.getCoreTag(), "Could not add splash view");
return;
}

splashImage.setAlpha(0f);
Expand Down
13 changes: 13 additions & 0 deletions android/capacitor/src/main/res/layout/fragment_bridge.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F0FF1414"
tools:context="com.getcapacitor.BridgeFragment">

<com.getcapacitor.CapacitorWebView
android:id="@+id/webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />

</FrameLayout>
6 changes: 6 additions & 0 deletions android/capacitor/src/main/res/values/attrs.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="bridge_fragment">
<attr name="start_dir" format="string"/>
</declare-styleable>
</resources>
7 changes: 7 additions & 0 deletions example/android/app/src/main/assets/page1/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<html>
<head>
</head>
<body>
<h1>Hello from HTML</h1>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.getcapacitor.myapp;

import android.net.Uri;
import android.os.Bundle;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager.widget.ViewPager;

import com.getcapacitor.BridgeFragment;

public class SimpleFragmentActivity extends AppCompatActivity implements BridgeFragment.OnFragmentInteractionListener {
private TextView mTextMessage;
private ViewPager mViewPager;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment_simple);
}



@Override
public void onFragmentInteraction(Uri uri) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeWidth="1"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<android.widget.FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:capacitor="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">

<fragment
class="com.getcapacitor.BridgeFragment"
android:id="@+id/bridge_fragment"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
capacitor:start_dir="page1"
/>


</android.widget.FrameLayout>
2 changes: 2 additions & 0 deletions example/android/app/src/main/res/values/dimens.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<resources>
<dimen name="fab_margin">16dp</dimen>
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>
5 changes: 5 additions & 0 deletions example/android/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,9 @@
<string name="app_name">MyApp</string>
<string name="title_activity_main">MyApp</string>
<string name="custom_url_scheme">com.getcapacitor.myapp</string>
<string name="title_activity_fragment">FragmentActivity</string>

<string name="title_home">Home</string>
<string name="title_dashboard">Dashboard</string>
<string name="title_notifications">Notifications</string>
</resources>
Loading

0 comments on commit a8d9591

Please sign in to comment.