Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ android/app/release/
node_modules/
npm-debug.log
yarn-error.log
# package-lock.json
package-lock.json

android/app/google-services.json
android/keystore.properties
Expand Down
45 changes: 42 additions & 3 deletions App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import React, {useEffect} from 'react';
// import 'react-native-reanimated';

import {StatusBar, StyleSheet} from 'react-native';
import {
StatusBar,
StyleSheet,
PermissionsAndroid,
Platform,
} from 'react-native';
import {SafeAreaProvider, SafeAreaView} from 'react-native-safe-area-context';
import Toast from 'react-native-toast-message';
import MainApp from './src/main';
Expand All @@ -14,8 +17,43 @@ enableScreens();
function App(): React.JSX.Element {
useEffect(() => {
SplashScreen.hide();
requestStoragePermission();
}, []);

const requestStoragePermission = async () => {
try {
if (Platform.OS === 'android') {
if (Platform.Version >= 33) {
// Android 13+ (Images, Video, Audio separately)
const result = await PermissionsAndroid.requestMultiple([
PermissionsAndroid.PERMISSIONS.READ_MEDIA_IMAGES,
PermissionsAndroid.PERMISSIONS.READ_MEDIA_VIDEO,
PermissionsAndroid.PERMISSIONS.READ_MEDIA_AUDIO,
]);
console.log('Android 13+ permission result:', result);
} else {
// Android 12 and below
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
{
title: 'Storage Permission Required',
message:
'ScheduleX needs access to your storage to function properly.',
buttonPositive: 'OK',
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log('Storage permission granted');
} else {
console.log('Storage permission denied');
}
}
}
} catch (err) {
console.warn(err);
}
};

return (
<SafeAreaProvider>
<SafeAreaView style={styles.safeArea} edges={['top']}>
Expand Down Expand Up @@ -44,4 +82,5 @@ const styles = StyleSheet.create({
backgroundColor: '#18181B',
},
});

export default App;
71 changes: 46 additions & 25 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,31 +1,52 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.schedulex">

<!-- Internet access -->
<uses-permission android:name="android.permission.INTERNET" />

<!-- For Android 10 and below -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29" />

<!-- For Android 11+ -->
<!-- Only use MANAGE_EXTERNAL_STORAGE if you need full access, else rely on SAF -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

<application
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="false"
android:theme="@style/AppTheme"
android:supportsRtl="true">

<activity
android:name=".MainActivity"
android:name=".MainApplication"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"
android:exported="true"
android:screenOrientation="portrait"
>

<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="false"
android:theme="@style/AppTheme"
android:supportsRtl="true">

<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"
android:exported="true"
android:screenOrientation="portrait">

</manifest>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<!-- FileProvider for exporting/importing CSV safely -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>

</application>
</manifest>
4 changes: 4 additions & 0 deletions android/app/src/main/res/xml/file_paths.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="." />
</paths>
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"react-native-linear-gradient": "^2.8.3",
"react-native-multiple-select": "^0.5.12",
"react-native-pager-view": "^6.8.1",
"react-native-permissions": "^5.4.2",
"react-native-safe-area-context": "^5.0.0",
"react-native-screens": "^4.3.0",
"react-native-share": "^12.1.0",
Expand Down
Empty file.
23 changes: 4 additions & 19 deletions src/utils/exportSchedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Alert, ToastAndroid, Platform, PermissionsAndroid } from 'react-native'
import Share from 'react-native-share';
import { generateRegisterCSV } from './csv-export';
import { CardInterface } from '../types/cards';
import { PermissionsHelper } from './permissions';

export interface ExportResult {
path: string;
Expand All @@ -25,25 +26,9 @@ export class ExportScheduleUtility {
}

private async checkStoragePermission(): Promise<boolean> {
if (Platform.OS === 'android') {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
{
title: 'Storage Permission',
message: 'App needs access to storage to save CSV files',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
}
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
} catch (err) {
console.warn(err);
return false;
}
}
return true; // iOS doesn't need explicit storage permission for app documents
// Use PermissionsHelper for consistent permission logic
const result = await PermissionsHelper.requestStoragePermission();
return result.granted;
}

private getAllRegisterIds(): number[] {
Expand Down
Loading