Compare commits

...

15 Commits

Author SHA1 Message Date
ammarahm-ed
26a61d4d06 mobile: check if module is loaded 2023-01-25 16:23:06 +05:00
ammarahm-ed
847fa3a3ad mobile: cleanup 2023-01-25 15:36:00 +05:00
ammarahm-ed
4ab949b398 mobile: update script 2023-01-25 14:59:23 +05:00
ammarahm-ed
ae43aa8c60 mobile: update 2023-01-25 14:46:07 +05:00
ammarahm-ed
edf0a5bed3 mobile: update 2023-01-25 13:56:11 +05:00
ammarahm-ed
f20b08edcf mobile: update 2023-01-24 19:09:18 +05:00
ammarahm-ed
51858199fd mobile: update 2023-01-24 19:08:46 +05:00
ammarahm-ed
26bc156323 mobile: update gradle config 2023-01-24 18:26:24 +05:00
ammarahm-ed
7907f03dd2 mobile: bump version 2023-01-24 17:42:42 +05:00
ammarahm-ed
5ca58d5e69 mobile: fix crash on loading split module 2023-01-24 17:33:05 +05:00
ammarahm-ed
8826145854 mobile: bump version 2023-01-24 16:42:38 +05:00
ammarahm-ed
6f8ae6f805 ci: add workflow file 2023-01-24 14:42:46 +05:00
ammarahm-ed
8dbc74f3c2 mobile: prepare for testing 2023-01-24 14:34:56 +05:00
ammarahm-ed
cae14c351b mobile: update flavors 2023-01-24 12:39:55 +05:00
ammarahm-ed
327cb9b208 mobile: load geckoview via split module loader 2023-01-23 11:27:23 +05:00
39 changed files with 1018 additions and 214 deletions

View File

@@ -0,0 +1,64 @@
name: Publish @notesnook/android-internal
on: workflow_dispatch
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 40
steps:
- name: Checkout
uses: actions/checkout@v2
- uses: actions/setup-node@master
with:
node-version: '16'
cache: 'npm'
cache-dependency-path: |
apps/mobile/package-lock.json
apps/web/package-lock.json
packages/core/package-lock.json
packages/crypto/package-lock.json
packages/crypto-worker/package-lock.json
packages/editor-mobile/package-lock.json
packages/editor/package-lock.json
packages/logger/package-lock.json
packages/streamable-fs/package-lock.json
packages/theme/package-lock.json
- name: Use specific Java version for the builds
uses: joschi/setup-jdk@v2
with:
java-version: '11'
architecture: 'x64'
- name: Install node modules
run: |
npm ci
- name: Make Gradlew Executable
run: cd apps/mobile/native/android && chmod +x ./gradlew
- name: Build unsigned app bundle
run: yarn release:android:bundle
- name: Sign app bundle for Playstore release
id: sign_app
uses: r0adkll/sign-android-release@v1
with:
releaseDirectory: apps/mobile/native/android/app/build/outputs/bundle/release
signingKeyBase64: ${{ secrets.SIGNING_KEY }}
alias: ${{ secrets.ALIAS }}
keyStorePassword: ${{ secrets.KEY_PASSWORD }}
keyPassword: ${{ secrets.KEY_PASSWORD }}
- name: Publish to Playstore
id: deploy
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
packageName: com.streetwriters.notesnook
releaseFile: ${{steps.sign_app.outputs.signedReleaseFile}}
track: internal
status: completed
whatsNewDirectory: apps/mobile/native/android/releasenotes/

View File

@@ -6,6 +6,11 @@ artifacts/
native/android/app/src/main/assets/
native/android/geckoview/src/main/jniLibs/arm64-v8a
native/android/geckoview/src/main/jniLibs/armeabi-v7a
native/android/geckoview/src/main/jniLibs/x86
native/android/geckoview/src/main/jniLibs/x86_64
*Issues.md
build_cache/
#

View File

@@ -45,6 +45,7 @@ import { useEditor } from "./tiptap/use-editor";
import { useEditorEvents } from "./tiptap/use-editor-events";
import { editorController } from "./tiptap/utils";
import { useLayoutEffect } from "react";
import { useIsGeckoViewEnabled } from "../../utils/split-module-loader";
const style: ViewStyle = {
height: "100%",
@@ -53,6 +54,7 @@ const style: ViewStyle = {
alignSelf: "center",
backgroundColor: "transparent"
};
const onShouldStartLoadWithRequest = (request: ShouldStartLoadRequest) => {
if (request.url.includes("https")) {
if (Platform.OS === "ios" && !request.isTopFrame) return true;
@@ -63,6 +65,7 @@ const onShouldStartLoadWithRequest = (request: ShouldStartLoadRequest) => {
}
};
let GeckoView: any = null;
const Editor = React.memo(
forwardRef<
{
@@ -89,7 +92,8 @@ const Editor = React.memo(
noToolbar,
noHeader
});
const { enabled: useGeckoView, loading, view } = useIsGeckoViewEnabled();
GeckoView = view.current;
useImperativeHandle(ref, () => ({
get: () => editor
}));
@@ -124,8 +128,12 @@ const Editor = React.memo(
const onError = useCallback(() => {
editor.setLoading(true);
if (useGeckoView) {
//@ts-ignore
editor.ref?.connectMessagingPort();
}
setTimeout(() => editor.setLoading(false), 10);
}, [editor]);
}, [editor, useGeckoView]);
useEffect(() => {
eSubscribeEvent("webview_reset", onError);
@@ -145,48 +153,71 @@ const Editor = React.memo(
editorController.current = editor;
}
return editor.loading ? null : (
return editor.loading || loading ? null : (
<>
<WebView
testID={notesnook.editor.id}
ref={editor.ref}
onLoad={editor.onLoad}
onRenderProcessGone={onError}
nestedScrollEnabled
onError={onError}
injectedJavaScriptBeforeContentLoaded={`
{!useGeckoView || editorId !== "" || !GeckoView ? (
<WebView
testID={notesnook.editor.id}
ref={editor.ref}
onLoad={editor.onLoad}
onRenderProcessGone={onError}
nestedScrollEnabled
onError={onError}
injectedJavaScriptBeforeContentLoaded={`
globalThis.readonly=${readonly};
globalThis.noToolbar=${noToolbar};
globalThis.noHeader=${noHeader};
`}
injectedJavaScript={`globalThis.sessionId="${editor.sessionId}";`}
javaScriptEnabled={true}
focusable={true}
setSupportMultipleWindows={false}
overScrollMode="never"
scrollEnabled={false}
keyboardDisplayRequiresUserAction={false}
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
cacheMode="LOAD_DEFAULT"
cacheEnabled={true}
domStorageEnabled={true}
bounces={false}
setBuiltInZoomControls={false}
setDisplayZoomControls={false}
allowFileAccess={true}
scalesPageToFit={true}
hideKeyboardAccessoryView={false}
allowsFullscreenVideo={true}
allowFileAccessFromFileURLs={true}
allowUniversalAccessFromFileURLs={true}
originWhitelist={["*"]}
source={{
uri: EDITOR_URI
}}
style={style}
autoManageStatusBarEnabled={false}
onMessage={onMessage || undefined}
/>
injectedJavaScript={`globalThis.sessionId="${editor.sessionId}";`}
javaScriptEnabled={true}
focusable={true}
setSupportMultipleWindows={false}
overScrollMode="never"
scrollEnabled={false}
keyboardDisplayRequiresUserAction={false}
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
cacheMode="LOAD_DEFAULT"
cacheEnabled={true}
domStorageEnabled={true}
bounces={false}
setBuiltInZoomControls={false}
setDisplayZoomControls={false}
allowFileAccess={true}
scalesPageToFit={true}
hideKeyboardAccessoryView={false}
allowsFullscreenVideo={true}
allowFileAccessFromFileURLs={true}
allowUniversalAccessFromFileURLs={true}
originWhitelist={["*"]}
source={{
uri: EDITOR_URI
}}
style={style}
autoManageStatusBarEnabled={false}
onMessage={onMessage || undefined}
/>
) : (
<GeckoView
//@ts-ignore
ref={editor.ref}
source={{
uri: EDITOR_URI
}}
onLoadingStart={(e) => {
console.log(e.nativeEvent);
}}
injectedJavaScript={`globalThis.sessionId="${editor.sessionId}";`}
style={style}
onLoadingFinish={editor.onLoad}
onMessagingDisconnected={() => {
//@ts-ignore
editor.ref?.connectMessagingPort();
}}
onLoadingError={onError}
onMessage={onMessage}
/>
)}
{editorId === "shareEditor" ? null : (
<AppSection editor={editor} editorId={editorId} />
)}

View File

@@ -31,7 +31,7 @@ import { sleep } from "../../../utils/time";
import { NoteType } from "../../../utils/types";
import { Settings } from "./types";
import { getResponse, randId, textInput } from "./utils";
import SettingsService from "../../../services/settings";
type Action = { job: string; id: string };
async function call(webview: RefObject<WebView | undefined>, action?: Action) {
@@ -78,13 +78,18 @@ class Commands {
focus = async () => {
if (!this.ref.current) return;
if (Platform.OS === "android") {
//this.ref.current?.requestFocus();
setTimeout(async () => {
if (!this.ref) return;
textInput.current?.focus();
await this.doAsync("editor.commands.focus()");
this.ref?.current?.requestFocus();
}, 1);
const isGeckoView = SettingsService.get().useGeckoView;
setTimeout(
async () => {
if (!this.ref) return;
textInput.current?.focus();
setTimeout(async () => {
this.ref?.current?.requestFocus();
await this.doAsync("editor.commands.focus()");
}, 10);
},
isGeckoView ? 100 : 1
);
} else {
await sleep(200);
await this.doAsync("editor.commands.focus()");

View File

@@ -273,6 +273,10 @@ export const useEditorEvents = (
const onMessage = useCallback(
(event: WebViewMessageEvent) => {
const data = event.nativeEvent.data;
if (data?.startsWith("Error")) {
console.log("WebView Error", data);
return;
}
const editorMessage = JSON.parse(data) as EditorMessage;
if (
editorMessage.sessionId !== editor.sessionId &&

View File

@@ -36,7 +36,7 @@ import { useTagStore } from "../../../stores/use-tag-store";
import { ThemeStore, useThemeStore } from "../../../stores/use-theme-store";
import { eClearEditor, eOnLoadNote } from "../../../utils/events";
import { tabBarRef } from "../../../utils/global-refs";
import { timeConverter } from "../../../utils/time";
import { sleep, timeConverter } from "../../../utils/time";
import { NoteType } from "../../../utils/types";
import Commands from "./commands";
import { Content, EditorState, Note, SavePayload } from "./types";
@@ -52,6 +52,7 @@ import {
} from "./utils";
import { EVENTS } from "@notesnook/core/common";
import SettingsService from "../../../services/settings";
import { useSettingStore } from "../../../stores/use-setting-store";
export const useEditor = (
editorId = "",
@@ -59,6 +60,7 @@ export const useEditor = (
onChange?: (html: string) => void,
theme?: ThemeStore["colors"]
) => {
const useGeckoView = useSettingStore((state) => state.settings.useGeckoView);
const [loading, setLoading] = useState(false);
const [sessionId, setSessionId] = useState<string>(makeSessionId());
const sessionIdRef = useRef(sessionId);
@@ -119,11 +121,12 @@ export const useEditor = (
);
const onReady = useCallback(async () => {
if (useGeckoView) await sleep(3000);
if (!(await isEditorLoaded(editorRef, sessionIdRef.current))) {
overlay(true);
setLoading(true);
}
}, [overlay]);
}, [overlay,useGeckoView]);
useEffect(() => {
state.current.saveCount = 0;

View File

@@ -34,12 +34,13 @@ import { DDS } from "../../services/device-detection";
import { useSettingStore } from "../../stores/use-setting-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { editorRef } from "../../utils/global-refs";
import { useIsGeckoViewEnabled } from "../../utils/split-module-loader";
import { ProgressBar } from "./progress";
import { editorController, editorState, textInput } from "./tiptap/utils";
export const EditorWrapper = ({ width }) => {
const colors = useThemeStore((state) => state.colors);
const deviceMode = useSettingStore((state) => state.deviceMode);
const loading = false;
const { loading } = useIsGeckoViewEnabled();
const insets = useGlobalSafeAreaInsets();
const floating = useIsFloatingKeyboard();
const introCompleted = useSettingStore(

View File

@@ -22,6 +22,7 @@ import { AccentColorPicker, HomagePageSelector } from "./appearance";
import { AutomaticBackupsSelector } from "./backup-restore";
import DebugLogs from "./debug";
import { ConfigureToolbar } from "./editor/configure-toolbar";
import { GeckoViewLoader } from "./gecko-view-loader";
import SoundPicker from "./sound-picker";
import { Subscription } from "./subscription";
export const components: { [name: string]: ReactElement } = {
@@ -31,5 +32,6 @@ export const components: { [name: string]: ReactElement } = {
subscription: <Subscription />,
configuretoolbar: <ConfigureToolbar />,
"debug-logs": <DebugLogs />,
"sound-picker": <SoundPicker />
"sound-picker": <SoundPicker />,
"gecko-view-loader": <GeckoViewLoader />
};

View File

@@ -18,10 +18,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import Clipboard from "@react-native-clipboard/clipboard";
import { LogMessage } from "@streetwriters/logger";
import { LogMessage } from "@notesnook/logger";
import { format, LogLevel, logManager } from "@notesnook/core/logger";
import React, { useEffect, useState } from "react";
import { FlatList, Platform, TouchableOpacity, View } from "react-native";
import { Platform, TouchableOpacity, View } from "react-native";
import { FlashList } from "@shopify/flash-list";
import * as ScopedStorage from "react-native-scoped-storage";
import RNFetchBlob from "rn-fetch-blob";
import Storage from "../../common/database/storage";
@@ -226,110 +227,102 @@ export default function DebugLogs() {
/>
</View>
{currentLog && (
<FlatList
ListHeaderComponent={
<View
style={{
paddingHorizontal: 12,
marginBottom: 10,
flexDirection: "row",
alignItems: "center",
backgroundColor: colors.bg,
justifyContent: "space-between"
}}
>
<View
style={{
flexDirection: "row",
alignItems: "center"
}}
>
<Paragraph>{currentLog.key}</Paragraph>
<IconButton
customStyle={{
width: 30,
height: 30,
marginHorizontal: 5
}}
onPress={() => {
const index = logs.findIndex(
(l) => l.key === currentLog.key
);
if (index === 0) return;
setCurrentLog(logs[index - 1]);
}}
size={20}
name="chevron-left"
color={colors.icon}
/>
<IconButton
customStyle={{
width: 30,
height: 30
}}
onPress={() => {
const index = logs.findIndex(
(l) => l.key === currentLog.key
);
if (index === logs.length - 1) return;
setCurrentLog(logs[index + 1]);
}}
size={20}
name="chevron-right"
color={colors.icon}
/>
</View>
<View
style={{
flexDirection: "row"
}}
>
<IconButton
onPress={copyLogs}
size={20}
customStyle={{
width: 30,
height: 30,
marginRight: 5
}}
name="content-copy"
color={colors.gray}
/>
<IconButton
onPress={downloadLogs}
customStyle={{
width: 30,
height: 30,
marginRight: 5
}}
size={20}
name="download"
color={colors.gray}
/>
<IconButton
onPress={clearLogs}
customStyle={{
width: 30,
height: 30,
marginRight: 5
}}
size={20}
name="delete"
color={colors.gray}
/>
</View>
</View>
}
{currentLog ? (
<View
style={{
flex: 1,
width: "100%"
paddingHorizontal: 12,
marginBottom: 10,
flexDirection: "row",
alignItems: "center",
backgroundColor: colors.bg,
justifyContent: "space-between"
}}
stickyHeaderIndices={[0]}
>
<View
style={{
flexDirection: "row",
alignItems: "center"
}}
>
<Paragraph>{currentLog?.key}</Paragraph>
<IconButton
customStyle={{
width: 30,
height: 30,
marginHorizontal: 10
}}
onPress={() => {
const index = logs.findIndex((l) => l.key === currentLog?.key);
if (index === 0) return;
setCurrentLog(logs[index - 1]);
}}
size={24}
name="chevron-left"
color={colors.icon}
/>
<IconButton
customStyle={{
width: 30,
height: 30
}}
onPress={() => {
const index = logs.findIndex((l) => l.key === currentLog?.key);
if (index === logs.length - 1) return;
setCurrentLog(logs[index + 1]);
}}
size={24}
name="chevron-right"
color={colors.icon}
/>
</View>
<View
style={{
flexDirection: "row"
}}
>
<IconButton
onPress={copyLogs}
size={24}
customStyle={{
width: 30,
height: 30,
marginRight: 10
}}
name="content-copy"
color={colors.gray}
/>
<IconButton
onPress={downloadLogs}
customStyle={{
width: 30,
height: 30,
marginRight: 10
}}
size={24}
name="download"
color={colors.gray}
/>
<IconButton
onPress={clearLogs}
customStyle={{
width: 30,
height: 30,
marginRight: 10
}}
size={24}
name="delete"
color={colors.gray}
/>
</View>
</View>
) : null}
{currentLog ? (
<FlashList
ListFooterComponent={
<View
style={{
@@ -339,8 +332,9 @@ export default function DebugLogs() {
}
data={currentLog.logs}
renderItem={renderItem}
estimatedItemSize={120}
/>
)}
) : null}
</View>
);
}

View File

@@ -0,0 +1,142 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useEffect } from "react";
import { View } from "react-native";
import { Button } from "../../components/ui/button";
import { ProgressBarComponent } from "../../components/ui/svg/lazy";
import Paragraph from "../../components/ui/typography/paragraph";
import { ToastEvent } from "../../services/event-manager";
import SettingsService from "../../services/settings";
import { useThemeStore } from "../../stores/use-theme-store";
import { formatBytes } from "../../utils";
import { SIZE } from "../../utils/size";
import {
SplitModuleLoader,
useIsGeckoViewEnabled,
useSplitInstallSessionState
} from "../../utils/split-module-loader";
import { toCamelCase } from "../notes/common";
export const GeckoViewLoader = () => {
const { enabled, installed } = useIsGeckoViewEnabled();
const colors = useThemeStore((state) => state.colors);
const state = useSplitInstallSessionState();
useEffect(() => {
if (state?.status === "installed") {
SettingsService.set({
useGeckoView: true
});
}
}, [state?.status]);
return (
<View
style={{
width: "100%",
marginTop: 10
}}
>
<>
{state && !installed ? null : (
<Paragraph
style={{ marginTop: 10, marginBottom: 10 }}
color={colors.icon}
size={SIZE.sm}
>
{installed
? "GeckoView is already downloaded & installed on this device."
: "Installing GeckoView will download additional data on your phone."}
</Paragraph>
)}
{!installed && state ? null : (
<>
{!installed ? (
<Button
title="Install GeckoView"
type="accent"
style={{
borderRadius: 100,
width: 250,
alignSelf: "flex-start"
}}
onPress={async () => {
try {
SettingsService.set({
useGeckoView: true
});
await SplitModuleLoader.installModule("geckoview");
} catch (e) {
ToastEvent.error(e as Error);
}
}}
/>
) : (
<Button
title={enabled ? "Disable GeckoView" : "Enable GeckoView"}
type="accent"
style={{
borderRadius: 100,
width: 250,
alignSelf: "flex-start"
}}
onPress={() => {
SettingsService.set({
useGeckoView: !enabled
});
}}
/>
)}
</>
)}
{!installed && state ? (
<>
<Paragraph
style={{ marginTop: 10, marginBottom: 5 }}
color={colors.icon}
size={SIZE.xs + 1}
>
{toCamelCase(state.status)}
{state.status === "downloading"
? ` (${formatBytes(state.downloaded)}/${formatBytes(
state.total
)})`
: ""}
</Paragraph>
<ProgressBarComponent
height={5}
width={300}
animated={true}
useNativeDriver
indeterminate={state.status !== "downloading"}
progress={
state.status === "downloading"
? (state?.downloaded || 0) / (state?.total || 0)
: undefined
}
unfilledColor={colors.nav}
color={colors.accent}
borderWidth={0}
/>
</>
) : null}
</>
</View>
);
};

View File

@@ -949,9 +949,26 @@ export const settingsGroups: SettingSection[] = [
"Close and reopen the current opened note or restart the app for changes to take affect."
});
}
},
{
id: "experimental-features",
type: "screen",
name: "Experimental features",
description: "Use these features with caution",
sections: [
{
id: "use-gecko-view",
type: "component",
name: "Install GeckoView",
description:
"If you edit large notes on your phone & have experienced lags & slow performance, you can use GeckoView for the editor which performs many times better than the default Android System WebView.",
component: "gecko-view-loader"
}
]
}
]
},
{
id: "help-support",
name: "Help and support",

View File

@@ -73,6 +73,7 @@ export type Settings = {
corsProxy: string;
disableRealtimeSync?: boolean;
notificationSound?: Sound & { platform: PlatformOSType };
useGeckoView?:boolean
};
type DimensionsType = {

View File

@@ -0,0 +1,109 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { NativeModules, DeviceEventEmitter } from "react-native";
import { useState, useEffect, useRef } from "react";
import { useSettingStore } from "../../stores/use-setting-store";
import { Config } from "react-native-config";
import { DatabaseLogger } from "../../common/database";
interface SplitInstallSessionState {
status:
| "pending"
| "downloading"
| "downloaded"
| "installing"
| "installed"
| "failed"
| "cancelled"
| "requires_user_confirmation"
| "user_permission_granted"
| "user_permission_canceled"
| "canceling";
total?: number;
downloaded?: number;
errorCode?: number;
}
export const useSplitInstallSessionState = () => {
const [state, setState] = useState<SplitInstallSessionState | undefined>();
useEffect(() => {
const subscription = DeviceEventEmitter.addListener(
"onModuleLoaderStateUpdate",
(data: SplitInstallSessionState) => {
DatabaseLogger.log("onModuleLoaderStateUpdate" + data?.status);
setState(data);
}
);
return () => {
subscription?.remove();
};
}, []);
return state;
};
export const SplitModuleLoader: {
installModule: (name: string) => Promise<number>;
getInstalledModules: () => Promise<string[]>;
} = NativeModules.SplitModuleLoader;
export const useIsGeckoViewEnabled = () => {
const [loading, setLoading] = useState(true);
const [enabled, setEnabled] = useState(false);
const useGeckoView = useSettingStore((state) => state.settings.useGeckoView);
const ref = useRef();
const state = useSplitInstallSessionState();
useEffect(() => {
if (Config.GITHUB_RELEASE === "true") {
if (Config.enableGecko === "true") {
ref.current = require("@ammarahmed/react-native-geckoview").default;
setEnabled(true);
DatabaseLogger.log("Using GeckoView");
} else {
DatabaseLogger.log("Using Android WebView");
}
setLoading(false);
return;
}
SplitModuleLoader?.getInstalledModules()
.then((modules) => {
if (modules?.includes("geckoview")) {
ref.current = require("@ammarahmed/react-native-geckoview").default;
console.log(modules);
setEnabled(true);
DatabaseLogger.log("Using GeckoView");
} else {
DatabaseLogger.log("Using Android WebView");
}
setLoading(false);
})
.catch((e) => {
setLoading(false);
setEnabled(false);
DatabaseLogger.error(e);
DatabaseLogger.log("Using Android WebView");
});
}, [loading, useGeckoView, state?.status]);
return {
enabled: enabled && useGeckoView,
loading: loading,
installed: enabled,
view: ref
};
};

View File

@@ -160,12 +160,13 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled true
versionCode 2024
versionCode 2031
versionName getNpmVersion()
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
missingDimensionStrategy "store", "play"
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
buildConfigField "boolean", "IS_GITHUB_RELEASE", isGithubRelease().toString()
if (isNewArchitectureEnabled()) {
// We configure the NDK build only if you decide to opt-in for the New Architecture.
externalNativeBuild {
@@ -193,7 +194,7 @@ android {
}
}
}
if (isNewArchitectureEnabled()) {
// We configure the NDK build only if you decide to opt-in for the New Architecture.
externalNativeBuild {
@@ -234,14 +235,27 @@ android {
}
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include (*reactNativeArchitectures())
if (isGithubRelease()) {
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include (*reactNativeArchitectures())
}
}
} else {
dynamicFeatures = [':geckoview']
bundle {
abi {
enableSplit true
}
}
}
signingConfigs {
debug {
storeFile file('debug.keystore')
@@ -256,6 +270,7 @@ android {
keyPassword 'android'
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
@@ -272,32 +287,53 @@ android {
}
}
if (!isGeckoEnabled()) {
packagingOptions {
exclude "lib/**/libmozglue.so"
exclude "lib/**/libxul.so"
exclude "lib/**/libnss3.so"
exclude "lib/**/libnssckbi.so"
exclude "lib/**/libmozavutil.so"
exclude "lib/**/libsoftokn3.so"
exclude "lib/**/libmozavcodec.so"
exclude "lib/**/libipcclientcerts.so"
exclude "lib/**/libfreebl3.so"
exclude "lib/**/libplugin-container.so"
//exclude "lib/**/liblgpllibs.so"
}
}
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
applicationVariants.all { variant -> {
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// https://developer.android.com/studio/build/configure-apk-splits.html
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
if (isGithubRelease()) {
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
}
} else {
output.versionCodeOverride = 4 * 1048576 + defaultConfig.versionCode
}
}
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+" // From node_modules
if (!isGithubRelease()) {
implementation 'com.google.android.play:feature-delivery:2.0.1'
}
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'com.squareup.okhttp3:okhttp:4.9.2'
@@ -361,8 +397,21 @@ task copyFiles(type: Copy) {
from '../../../../../packages/editor-mobile/build.bundle'
into './src/main/assets'
}
task deleteFilesMessaging(type: Delete) {
delete './src/main/assets/messaging'
}
task copyFilesMessaging(type: Copy) {
from '../../../node_modules/@ammarahmed/react-native-geckoview/extensions'
into './src/main/assets'
}
copyFilesMessaging.dependsOn(deleteFilesMessaging);
preBuild.dependsOn(copyFilesMessaging);
copyFiles.dependsOn(deleteFiles)
preBuild.dependsOn(copyFiles)
preBuild.dependsOn(copyFiles);
project.ext.vectoricons = [
@@ -382,4 +431,12 @@ def isNewArchitectureEnabled() {
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
}
def isGithubRelease() {
return project.hasProperty("GITHUB_RELEASE") && project.GITHUB_RELEASE == "true"
}
def isGeckoEnabled() {
return project.hasProperty("enableGecko") && project.enableGecko == "true"
}

View File

@@ -57,4 +57,8 @@
-keep class org.apache.commons.io.** { *; }
# Background fetch
-keep class com.transistorsoft.rnbackgroundfetch.HeadlessTask { *; }
-keep class com.transistorsoft.rnbackgroundfetch.HeadlessTask { *; }
# SplitModuleLoader
-keep class com.streetwriters.notesnook.** { *; }
-keep class com.google.android.play.core.** { *; }

View File

@@ -152,12 +152,12 @@
android:resource="@xml/file_viewer_provider_paths" />
</provider>
<receiver android:exported="true" android:name=".BootRecieverService" >
<!-- <receiver android:exported="true" android:name=".BootRecieverService" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
</receiver> -->
</application>

View File

@@ -1,6 +1,5 @@
package com.streetwriters.notesnook;
import android.app.Application;
import android.content.Context;
import android.util.Log;
@@ -18,8 +17,6 @@ import java.util.Map;
import androidx.multidex.MultiDexApplication;
import com.facebook.react.bridge.JavaScriptExecutorFactory;
//import com.facebook.react.modules.systeminfo.AndroidInfoHelpers;
import com.facebook.react.TurboReactPackage;
import com.facebook.react.module.model.ReactModuleInfo;
import com.facebook.react.module.model.ReactModuleInfoProvider;
@@ -34,10 +31,8 @@ import com.streetwriters.notesnook.newarchitecture.MainApplicationReactNativeHos
import cl.json.RNShareModule;
import px.tooltips.RNTooltipsModule;
//import io.csie.kudo.reactnative.v8.executor.V8ExecutorFactory;
import androidx.annotation.Nullable;
public class MainApplication extends MultiDexApplication implements ReactApplication {
private final ReactNativeHost mNewArchitectureNativeHost =
new MainApplicationReactNativeHost(this);
@@ -112,6 +107,24 @@ public class MainApplication extends MultiDexApplication implements ReactApplica
}
});
if (!BuildConfig.IS_GITHUB_RELEASE) {
try {
/**
* We use reflection here because SplitModulePackage & PlayCore libraries are not
* available in Github/Fdroid release.
*/
Class<?> SplitCompat = Class.forName("com.google.android.play.core.splitcompat.SplitCompat");
SplitCompat.getMethod("install", Context.class)
.invoke(null, this.getApplication());
Class<?> SplitModulePackage = Class.forName("com.streetwriters.notesnook.SplitModulePackage");
packages.add((ReactPackage) SplitModulePackage.getConstructor().newInstance());
} catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
}
return packages;
}
@@ -140,13 +153,7 @@ public class MainApplication extends MultiDexApplication implements ReactApplica
aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}

View File

@@ -0,0 +1,165 @@
package com.streetwriters.notesnook;
import android.app.Activity;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.google.android.play.core.splitcompat.SplitCompat;
import com.google.android.play.core.splitinstall.SplitInstallException;
import com.google.android.play.core.splitinstall.SplitInstallManager;
import com.google.android.play.core.splitinstall.SplitInstallManagerFactory;
import com.google.android.play.core.splitinstall.SplitInstallRequest;
import com.google.android.play.core.splitinstall.SplitInstallSessionState;
import com.google.android.play.core.splitinstall.SplitInstallStateUpdatedListener;
import com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus;
@ReactModule(name = "SplitModuleLoader")
public class SplitModuleLoader extends ReactContextBaseJavaModule implements SplitInstallStateUpdatedListener {
ReactContext rc;
SplitInstallManager manager;
ActivityEventListener listener = null;
static int REQUEST_CODE = 25609;
public SplitModuleLoader(ReactContext context) {
rc = context;
manager = SplitInstallManagerFactory.create(rc);
}
@ReactMethod
public void installModule(String name, Promise promise) {
SplitInstallRequest request = SplitInstallRequest.newBuilder().addModule(name).build();
manager.startInstall(request).addOnFailureListener(e -> {
Toast.makeText(rc,((SplitInstallException) e).getMessage(),Toast.LENGTH_LONG);
promise.reject(e);
}).addOnSuccessListener(integer -> {
promise.resolve(integer);
});
manager.registerListener(this);
}
@ReactMethod
public void getInstalledModules(Promise promise) {
try {
WritableArray array = Arguments.createArray();
for (String item: manager.getInstalledModules()) {
array.pushString(item);
}
promise.resolve(array);
} catch (Exception e) {
promise.reject(e);
}
}
@ReactMethod
public void queryClass(String name, Promise promise) {
try {
String n = Class.forName(name).getName();
promise.resolve(n);
} catch (ClassNotFoundException e) {
promise.resolve("CLASS NOT FOUND");
}
}
@Override
public void onCatalystInstanceDestroy() {
super.onCatalystInstanceDestroy();
manager.unregisterListener(this);
}
@NonNull
@Override
public String getName() {
return "SplitModuleLoader";
}
@Override
public void onStateUpdate(@NonNull SplitInstallSessionState splitInstallSessionState) {
WritableMap map = Arguments.createMap();
switch (splitInstallSessionState.status()) {
case SplitInstallSessionStatus.UNKNOWN:
case SplitInstallSessionStatus.PENDING:
map.putString("status", "pending");
case SplitInstallSessionStatus.DOWNLOADING:
long total = splitInstallSessionState.totalBytesToDownload();
long downloaded = splitInstallSessionState.bytesDownloaded();
map.putDouble("total", total);
map.putDouble("downloaded", downloaded);
map.putString("status", "downloading");
break;
case SplitInstallSessionStatus.DOWNLOADED:
map.putString("status", "downloaded");
break;
case SplitInstallSessionStatus.INSTALLING:
map.putString("status", "installing");
break;
case SplitInstallSessionStatus.INSTALLED:
map.putString("status", "installed");
break;
case SplitInstallSessionStatus.FAILED:
map.putString("status", "failed");
map.putInt("errorCode", splitInstallSessionState.errorCode());
break;
case SplitInstallSessionStatus.CANCELED:
map.putString("status", "canceled");
break;
case SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION:
map.putString("status", "requires_user_confirmation");
try {
if (listener != null) {
rc.removeActivityEventListener(listener);
listener = null;
}
listener = new ActivityEventListener() {
@Override
public void onActivityResult(Activity activity, int code, int result, @Nullable Intent intent) {
if (listener != null) {
rc.removeActivityEventListener(listener);
}
if (code == REQUEST_CODE && result == Activity.RESULT_OK) {
WritableMap map = Arguments.createMap();
map.putString("status", "user_permission_granted");
dispatchEvent("onModuleLoaderStateUpdate", map);
} else if (code == REQUEST_CODE && result == Activity.RESULT_CANCELED) {
WritableMap map = Arguments.createMap();
map.putString("status", "user_permission_canceled");
dispatchEvent("onModuleLoaderStateUpdate", map);
}
}
@Override
public void onNewIntent(Intent intent) {
}
};
rc.addActivityEventListener(listener);
manager.startConfirmationDialogForResult(splitInstallSessionState,getCurrentActivity(),REQUEST_CODE);
} catch (Exception e) {
}
break;
case SplitInstallSessionStatus.CANCELING:
map.putString("status", "canceling");
break;
}
dispatchEvent("onModuleLoaderStateUpdate", map);
}
protected void dispatchEvent(String eventName, WritableMap event) {
rc.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, event);
}
}

View File

@@ -0,0 +1,23 @@
package com.streetwriters.notesnook;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class SplitModulePackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Arrays.asList(new SplitModuleLoader(reactContext));
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}

View File

@@ -3,5 +3,5 @@
<string name="title_activity_share">NotesnookShare</string>
<string name="appwidget_text">EXAMPLE</string>
<string name="add_widget">Add widget</string>
<string name="title_dynamic_geckoview">geckoview</string>
</resources>

View File

@@ -12,6 +12,7 @@ buildscript {
androidXCoreVersion = "1.7.0"
androidXCore = "1.7.0"
androidXBrowser = "1.0.0"
geckoviewVersion = "109.0.20230112150232"
if (System.properties['os.arch'] == "aarch64") {
// For M1 Users we need to use the NDK 24 which added support for aarch64
ndkVersion = "24.0.8215888"
@@ -72,5 +73,6 @@ allprojects {
maven {
url("${project(':react-native-background-fetch').projectDir}/libs")
}
maven { url "https://maven.mozilla.org/maven2/" }
}
}

View File

@@ -0,0 +1,100 @@
plugins {
id("com.android.dynamic-feature")
}
def DEFAULT_COMPILE_SDK_VERSION = 31
def DEFAULT_BUILD_TOOLS_VERSION = '31.0.0'
def DEFAULT_MIN_SDK_VERSION = 21
def DEFAULT_TARGET_SDK_VERSION = 31
// https://maven.mozilla.org/?prefix=maven2/org/mozilla/geckoview/geckoview/
def DEFAULT_GECKOVIEW_VERSION = "109.0.20230112150232"
def safeExtGet(prop, fallback) {
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}
android {
compileSdkVersion safeExtGet('compileSdkVersion', DEFAULT_COMPILE_SDK_VERSION)
buildToolsVersion safeExtGet('buildToolsVersion', DEFAULT_BUILD_TOOLS_VERSION)
defaultConfig {
minSdkVersion safeExtGet('minSdkVersion', DEFAULT_MIN_SDK_VERSION)
targetSdkVersion safeExtGet('targetSdkVersion', DEFAULT_TARGET_SDK_VERSION)
missingDimensionStrategy "store", "play"
}
lintOptions {
abortOnError false
}
buildTypes {
release {
minifyEnabled false
}
}
configurations {
extractSO
}
packagingOptions {
exclude "lib/**/liblgpllibs.so"
}
}
repositories {
// ref: https://www.baeldung.com/maven-local-repository
mavenLocal()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
maven {
// Android JSC is installed from npm
url "$rootDir/../node_modules/jsc-android/dist"
}
google()
mavenCentral()
jcenter()
}
dependencies {
implementation project(':app')
def geckoviewVersion = safeExtGet('geckoviewVersion', DEFAULT_GECKOVIEW_VERSION)
//noinspection GradleDynamicVersion
extractSO("org.mozilla.geckoview:geckoview:${geckoviewVersion}")
}
task extractSOFiles {
def soFilesExist = new File("${buildDir}/../src/main/jniLibs/arm64-v8a/libxul.so").exists();
if (soFilesExist) {
println("GeckoView .so files already copied to jniLibs folder");
return;
}
doLast {
configurations.extractSO.files.each {
def file = it.absoluteFile
copy {
from (zipTree(file)) {
include "jni/**"
eachFile { fcd ->
fcd.relativePath = new RelativePath(true, fcd.relativePath.segments.drop(1))
}
includeEmptyDirs = false
}
into "${buildDir}/../src/main/jniLibs"
}
// copy {
// from (zipTree(file)) {
// include "assets/**"
// eachFile { fcd ->
// fcd.relativePath = new RelativePath(true, fcd.relativePath.segments.drop(1))
// }
// includeEmptyDirs = false
// }
// into "${buildDir}/../src/main/assets"
// }
}
}
}
tasks.whenTaskAdded { task ->
task.dependsOn(extractSOFiles)
}

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dist="http://schemas.android.com/apk/distribution"
package="com.geckonative.module"
split="geckoview-module">
<dist:module
dist:instant="false"
dist:title="@string/title_dynamic_geckoview">
<dist:delivery>
<dist:on-demand />
<!-- <dist:install-time /> -->
</dist:delivery>
<dist:fusing dist:include="true" />
</dist:module>
<application android:hasCode="false">
</application>
</manifest>

View File

@@ -42,4 +42,4 @@ newArchEnabled=false
# V8 Cache Mode Config
# v8.cacheMode=none
# v8.android.tools.dir=/home/ammarahm-ed/Repos/notesnook-mobile/node_modules/v8-android-jit-nointl/dist/tools/android
# v8.android.tools.dir=/home/ammarahm-ed/Repos/notesnook-mobile/node_modules/v8-android-jit-nointl/dist/tools/android

View File

@@ -8,3 +8,7 @@ if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true")
include(":ReactAndroid:hermes-engine")
project(":ReactAndroid:hermes-engine").projectDir = file('../../node_modules/react-native/ReactAndroid/hermes-engine')
}
if (!settings.hasProperty("GITHUB_RELEASE")) {
include ':geckoview'
}

View File

@@ -1087,7 +1087,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2024;
CURRENT_PROJECT_VERSION = 2026;
DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1161,7 +1161,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.4.1;
MARKETING_VERSION = 2.4.3;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -1191,7 +1191,7 @@
CODE_SIGN_ENTITLEMENTS = Notesnook/Notesnook.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2024;
CURRENT_PROJECT_VERSION = 2026;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
@@ -1264,7 +1264,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.4.1;
MARKETING_VERSION = 2.4.3;
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = (
"$(inherited)",
@@ -1422,7 +1422,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2024;
CURRENT_PROJECT_VERSION = 2026;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1434,7 +1434,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.1;
MARKETING_VERSION = 2.4.3;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
@@ -1464,7 +1464,7 @@
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2024;
CURRENT_PROJECT_VERSION = 2026;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1476,7 +1476,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.1;
MARKETING_VERSION = 2.4.3;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1505,7 +1505,7 @@
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2024;
CURRENT_PROJECT_VERSION = 2026;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1579,7 +1579,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.1;
MARKETING_VERSION = 2.4.3;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
@@ -1609,7 +1609,7 @@
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2024;
CURRENT_PROJECT_VERSION = 2026;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1683,7 +1683,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.1;
MARKETING_VERSION = 2.4.3;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -57,7 +57,8 @@
"@react-native-community/datetimepicker":"6.6.0",
"react-native-date-picker": "4.2.6",
"react-native-notification-sounds": "0.5.5",
"react-native-background-fetch": "4.1.7"
"react-native-background-fetch": "4.1.7",
"@ammarahmed/react-native-geckoview": "^1.4.0"
},
"devDependencies": {
"@babel/core": "^7.12.9",

View File

@@ -1,5 +1,4 @@
const isGithubRelease = process.env.GITHUB_RELEASE;
const isGithubRelease = false;
const config = {
commands: require('@callstack/repack/commands'),
project: {

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/mobile",
"version": "2.4.0",
"version": "2.4.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@notesnook/mobile",
"version": "2.4.0",
"version": "2.4.1",
"license": "GPL-3.0-or-later",
"workspaces": [
"native/",
@@ -61,6 +61,7 @@
"license": "GPL-3.0-or-later",
"dependencies": {
"@ammarahmed/notifee-react-native": "7.3.1",
"@ammarahmed/react-native-geckoview": "^1.4.0",
"@ammarahmed/react-native-sodium": "1.2.0",
"@callstack/repack": "^3.0.0",
"@react-native-clipboard/clipboard": "^1.9.0",
@@ -162,6 +163,15 @@
"react-native": "*"
}
},
"node_modules/@ammarahmed/react-native-geckoview": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@ammarahmed/react-native-geckoview/-/react-native-geckoview-1.4.0.tgz",
"integrity": "sha512-ys4dSZS+Dtw6mHeHVOy3/16uOhU+b+kb5PJL/wfqUhccmeipJK46qPVkqBft1YdxJ/4cc6W/4nIGXRpeCivESQ==",
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/@ammarahmed/react-native-sodium": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ammarahmed/react-native-sodium/-/react-native-sodium-1.2.0.tgz",
@@ -21520,6 +21530,11 @@
"resolved": "https://registry.npmjs.org/@ammarahmed/notifee-react-native/-/notifee-react-native-7.3.1.tgz",
"integrity": "sha512-kzBDw2NAt9kGD0CJ193JME4bS4uQolY+UubGiTnJwHfaLH7dFNju6RC15/u8tzG59ebDuCalkMLr+rAONtg2Rw=="
},
"@ammarahmed/react-native-geckoview": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@ammarahmed/react-native-geckoview/-/react-native-geckoview-1.4.0.tgz",
"integrity": "sha512-ys4dSZS+Dtw6mHeHVOy3/16uOhU+b+kb5PJL/wfqUhccmeipJK46qPVkqBft1YdxJ/4cc6W/4nIGXRpeCivESQ=="
},
"@ammarahmed/react-native-sodium": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ammarahmed/react-native-sodium/-/react-native-sodium-1.2.0.tgz",
@@ -24317,6 +24332,7 @@
"version": "file:native",
"requires": {
"@ammarahmed/notifee-react-native": "7.3.1",
"@ammarahmed/react-native-geckoview": "^1.4.0",
"@ammarahmed/react-native-sodium": "1.2.0",
"@babel/core": "^7.12.9",
"@babel/eslint-parser": "^7.16.5",

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "2.4.1",
"version": "2.4.8",
"private": true,
"license": "GPL-3.0-or-later",
"workspaces": [
@@ -19,8 +19,10 @@
"e2e-android": "cd native && detox test --configuration android.emu.release --detectOpenHandles",
"e2e-ios": "cd native && detox test -c ios.sim.release --detectOpenHandles",
"bump": "cd native && npx react-native bump-version --skip-semver-for android",
"release-android": "cd native/android && GITHUB_RELEASE=true ENVFILE=.env.public ./gradlew assembleRelease --no-daemon",
"release-android-bundle": "cd native/android && ./gradlew bundleRelease --no-daemon"
"release-android": "./scripts/gh-release.sh",
"release-gecko-android": "./scripts/gh-geckoview-release.sh",
"release-android-bundle": "cd native/android && ./gradlew bundleRelease --no-daemon",
"debug-dynamic-module": "./scripts/debug-dynamic-module.sh"
},
"devDependencies": {
"patch-package": "^6.4.7",

View File

@@ -0,0 +1,8 @@
tc() { set ${*,,} ; echo ${*^} ; }
adb reverse tcp:8081 tcp:8081 || true
rm -rf $(PWD)/native/android/app/build/outputs/bundle || true
cd native/android && ./gradlew bundle$2
cd ../../
java -jar $HOME/bundletool/bundletool-all-1.13.2.jar build-apks --overwrite --local-testing --bundle $(PWD)/native/android/app/build/outputs/bundle/$1/app-$1.aab --output $(PWD)/native/android/app/build/outputs/bundle/$0/apkset.apks
java -jar $HOME/bundletool/bundletool-all-1.13.2.jar install-apks --apks $(PWD)/native/android/app/build/outputs/bundle/$0/apkset.apks
adb shell monkey -p com.streetwriters.notesnook 1

View File

@@ -0,0 +1,8 @@
echo enableGecko=true >> $(PWD)/native/android/gradle.properties
echo GITHUB_RELEASE=true >> $(PWD)/native/android/gradle.properties
echo GITHUB_RELEASE=true > $(PWD)/native/.env
echo enableGecko=true >> $(PWD)/native/.env
rm $(PWD)/native/android/app/src/main/java/com/streetwriters/notesnook/SplitModuleLoader.java || true
rm $(PWD)/native/android/app/src/main/java/com/streetwriters/notesnook/SplitModulePackage.java || true
cd native/android
./gradlew assembleRelease --no-daemon

View File

@@ -0,0 +1,7 @@
echo GITHUB_RELEASE=true >> $(PWD)/native/android/gradle.properties
echo GITHUB_RELEASE=true > $(PWD)/native/.env
sed s/false/true/g < react-native.config.js > react-native.config.jss && mv react-native.config.jss react-native.config.js
rm $(PWD)/native/android/app/src/main/java/com/streetwriters/notesnook/SplitModuleLoader.java || true
rm $(PWD)/native/android/app/src/main/java/com/streetwriters/notesnook/SplitModulePackage.java || true
cd native/android
./gradlew assembleRelease --no-daemon

View File

@@ -449,6 +449,7 @@
},
"node_modules/@azure/msal-node-extensions": {
"version": "1.0.0-alpha.28",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@azure/msal-common": "^9.0.1",

View File

@@ -13,11 +13,13 @@
"test:web": "nx test @notesnook/web",
"test:core": "nx test @notesnook/core",
"start:android": "nx run-android @notesnook/mobile",
"start:android:dynamic-module": "nx debug-dynamic-module @notesnook/mobile",
"start:ios": "nx run-ios @notesnook/mobile",
"prepare:ios": "nx install-pods @notesnook/mobile",
"build:ios": "nx build-ios @notesnook/mobile",
"build:android": "nx build-android @notesnook/mobile",
"release:android": "nx release-android @notesnook/mobile",
"release:android:gecko": "nx release-gecko-android @notesnook/mobile",
"release:android:bundle": "nx release-android-bundle @notesnook/mobile",
"test:ios": "nx e2e-ios @notesnook/mobile",
"test:android": "nx e2e-android @notesnook/mobile",

View File

@@ -13,6 +13,7 @@
body {
height: 100%;
font-family: "Open Sans";
background-color: var(--nn_bg);
}
p {