initial commit
This commit is contained in:
34
app/build.gradle
Normal file
34
app/build.gradle
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.mattintech.simplelogupload'
|
||||
compileSdkVersion 34
|
||||
defaultConfig {
|
||||
applicationId "com.mattintech.simplelogupload"
|
||||
minSdkVersion 30
|
||||
targetSdkVersion 33
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'com.google.android.material:material:1.8.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
}
|
||||
36
app/src/main/AndroidManifest.xml
Normal file
36
app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mattintech.simplelogupload">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@android:drawable/ic_menu_upload"
|
||||
android:label="SimpleLogUpload"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.AppCompat.DayNight.DarkActionBar"> <!-- Add this line -->
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Register the JobService -->
|
||||
<service
|
||||
android:name=".FileUploadJobService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||
|
||||
<service
|
||||
android:name=".FileUploadForegroundService"
|
||||
android:permission="android.permission.FOREGROUND_SERVICE" />
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.mattintech.simplelogupload;
|
||||
|
||||
public class Constant {
|
||||
public static final String APP_TAG = "SLU::";
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.mattintech.simplelogupload;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class FileUploadForegroundService extends Service {
|
||||
|
||||
private static final String TAG = Constant.APP_TAG + "FileUploadService";
|
||||
private static final String CHANNEL_ID = "FileUploadChannel";
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
Log.d(TAG, "Foreground Service started.");
|
||||
|
||||
// Create a notification and run the service in the foreground
|
||||
createNotificationChannel();
|
||||
Notification notification = createNotification();
|
||||
startForeground(1, notification);
|
||||
|
||||
Log.d(TAG, "Notification created.");
|
||||
|
||||
// Start file upload process
|
||||
File dir = new File(Environment.getExternalStorageDirectory(), "log");
|
||||
File mostRecentFile = findMostRecentFile(dir);
|
||||
if (mostRecentFile != null) {
|
||||
uploadFile(mostRecentFile);
|
||||
} else {
|
||||
Log.e(TAG, "No DUMPSTATE file found.");
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Notification createNotification() {
|
||||
Intent notificationIntent = new Intent(this, MainActivity.class);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
|
||||
|
||||
return new NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setContentTitle("File Upload")
|
||||
.setContentText("Uploading file in progress...")
|
||||
.setSmallIcon(R.drawable.logupload)
|
||||
.setContentIntent(pendingIntent)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void createNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
CharSequence name = "File Upload Channel";
|
||||
String description = "Notification channel for file upload";
|
||||
int importance = NotificationManager.IMPORTANCE_HIGH; // Change to HIGH to ensure visibility
|
||||
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
|
||||
channel.setDescription(description);
|
||||
|
||||
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
}
|
||||
|
||||
private File findMostRecentFile(File dir) {
|
||||
File[] files = dir.listFiles((d, name) -> name.matches("dumpState_.*\\.zip"));
|
||||
return (files != null && files.length > 0) ? Arrays.stream(files)
|
||||
.max(Comparator.comparingLong(File::lastModified))
|
||||
.orElse(null) : null;
|
||||
}
|
||||
|
||||
private void uploadFile(File file) {
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
Log.d(TAG, "Uploading file: " + file.getName() + ", Size: " + file.length());
|
||||
|
||||
RequestBody fileBody = RequestBody.create(file, MediaType.parse("application/zip"));
|
||||
MultipartBody requestBody = new MultipartBody.Builder()
|
||||
.setType(MultipartBody.FORM)
|
||||
.addFormDataPart("file", file.getName(), fileBody)
|
||||
.build();
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url("http://172.16.30.69:7777/upload") // Replace with your server's URL
|
||||
.post(requestBody)
|
||||
.build();
|
||||
|
||||
client.newCall(request).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "Upload failed: " + e.getMessage());
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) throws IOException {
|
||||
if (response.isSuccessful()) {
|
||||
String tinyUrl = response.body().string(); // Assuming the server returns tinyURL in response
|
||||
Log.d(TAG, "Upload successful! TinyURL: " + tinyUrl);
|
||||
} else {
|
||||
Log.e(TAG, "Upload failed, Response code: " + response.code());
|
||||
}
|
||||
stopSelf();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.mattintech.simplelogupload;
|
||||
|
||||
import android.app.job.JobParameters;
|
||||
import android.app.job.JobService;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class FileUploadJobService extends JobService {
|
||||
private static final String TAG = Constant.APP_TAG + "FileUploadJobService:";
|
||||
|
||||
@Override
|
||||
public boolean onStartJob(JobParameters params) {
|
||||
File dir = new File(Environment.getExternalStorageDirectory(), "log");
|
||||
File mostRecentFile = findMostRecentFile(dir);
|
||||
|
||||
if (mostRecentFile != null) {
|
||||
Toast.makeText(this, "Starting Upload", Toast.LENGTH_SHORT).show();
|
||||
uploadFile(mostRecentFile, params);
|
||||
} else {
|
||||
Toast.makeText(getApplicationContext(), "No DUMPSTATE file found.", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onStopJob(JobParameters params) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private File findMostRecentFile(File dir) {
|
||||
File[] files = dir.listFiles((d, name) -> name.matches("dumpState_.*\\.zip"));
|
||||
return (files != null && files.length > 0) ? Arrays.stream(files)
|
||||
.max(Comparator.comparingLong(File::lastModified))
|
||||
.orElse(null) : null;
|
||||
}
|
||||
private void uploadFile(File file, JobParameters params) {
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
Log.d(TAG, "Uploading file: " + file.getName() + ", Size: " + file.length());
|
||||
RequestBody fileBody = RequestBody.create(file, MediaType.parse("application/zip"));
|
||||
|
||||
MultipartBody requestBody = new MultipartBody.Builder()
|
||||
.setType(MultipartBody.FORM)
|
||||
.addFormDataPart("file", file.getName(), fileBody)
|
||||
.build();
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url("http://172.16.30.69:7777/upload") // Replace with your Flask server's URL
|
||||
.post(requestBody)
|
||||
.build();
|
||||
|
||||
client.newCall(request).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "Upload failed: " + e.getMessage());
|
||||
jobFinished(params, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) throws IOException {
|
||||
if (response.isSuccessful()) {
|
||||
String tinyUrl = response.body().string(); // Assuming the server returns tinyURL in response
|
||||
Log.d(TAG, "Upload successful! TinyURL: " + tinyUrl);
|
||||
|
||||
// Show tinyURL on the UI (using BroadcastReceiver or local storage to communicate with MainActivity)
|
||||
} else {
|
||||
Log.e(TAG, "Upload failed, Response code: " + response.code());
|
||||
}
|
||||
jobFinished(params, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
|
||||
package com.mattintech.simplelogupload;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.job.JobInfo;
|
||||
import android.app.job.JobScheduler;
|
||||
import android.app.job.JobService;
|
||||
import android.app.job.JobParameters;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.provider.Settings;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
public class MainActivity extends Activity {
|
||||
private static final int REQUEST_MANAGE_EXTERNAL_STORAGE = 101;
|
||||
private TextView resultView;
|
||||
private Button uploadButton;
|
||||
private File mostRecentDumpStateFile;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
resultView = findViewById(R.id.resultView);
|
||||
uploadButton = findViewById(R.id.uploadButton);
|
||||
|
||||
|
||||
// Check for All Files Access Permission
|
||||
requestNotificationPermission();
|
||||
if (!hasAllFilesAccessPermission()) {
|
||||
requestAllFilesAccessPermission();
|
||||
} else {
|
||||
checkForDumpStateAndUpdateUI();
|
||||
}
|
||||
|
||||
uploadButton.setOnClickListener(v -> {
|
||||
if (mostRecentDumpStateFile != null) {
|
||||
//scheduleJob();
|
||||
startFileUpload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void requestNotificationPermission() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(this,
|
||||
new String[]{Manifest.permission.POST_NOTIFICATIONS}, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForDumpStateAndUpdateUI() {
|
||||
// Find the most recent *DUMPSTATE*.zip file
|
||||
File dir = new File(Environment.getExternalStorageDirectory(), "log");
|
||||
mostRecentDumpStateFile = findMostRecentFile(dir);
|
||||
|
||||
if (mostRecentDumpStateFile != null) {
|
||||
resultView.setText("Dumpstate found: " + mostRecentDumpStateFile.getName());
|
||||
uploadButton.setVisibility(View.VISIBLE); // Show the button
|
||||
} else {
|
||||
resultView.setText("No dumpstate file found.");
|
||||
uploadButton.setVisibility(View.GONE); // Hide the button if no file found
|
||||
}
|
||||
}
|
||||
|
||||
private File findMostRecentFile(File dir) {
|
||||
File[] files = dir.listFiles((d, name) -> name.matches("dumpState_.*\\.zip")); // Regex to match dumpstate
|
||||
return (files != null && files.length > 0) ? Arrays.stream(files)
|
||||
.max(Comparator.comparingLong(File::lastModified))
|
||||
.orElse(null) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == REQUEST_MANAGE_EXTERNAL_STORAGE) {
|
||||
if (hasAllFilesAccessPermission()) {
|
||||
scheduleJob(); // Now you can proceed if permission is granted
|
||||
} else {
|
||||
Toast.makeText(this, "Permission not granted!", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasAllFilesAccessPermission() {
|
||||
return Build.VERSION.SDK_INT < Build.VERSION_CODES.R || Environment.isExternalStorageManager();
|
||||
}
|
||||
|
||||
private void requestAllFilesAccessPermission() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
try {
|
||||
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
|
||||
intent.setData(Uri.parse("package:" + getPackageName()));
|
||||
startActivityForResult(intent, REQUEST_MANAGE_EXTERNAL_STORAGE);
|
||||
} catch (Exception e) {
|
||||
// Fallback in case the direct intent fails (should not normally happen)
|
||||
Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
|
||||
startActivityForResult(intent, REQUEST_MANAGE_EXTERNAL_STORAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
private void scheduleJob() {
|
||||
ComponentName componentName = new ComponentName(this, FileUploadJobService.class);
|
||||
JobInfo info = new JobInfo.Builder(123, componentName)
|
||||
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
|
||||
.setPersisted(true)
|
||||
.build();
|
||||
|
||||
JobScheduler scheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
|
||||
scheduler.schedule(info);
|
||||
}
|
||||
|
||||
private void startFileUpload() {
|
||||
Intent serviceIntent = new Intent(this, FileUploadForegroundService.class);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
startForegroundService(serviceIntent);
|
||||
} else {
|
||||
startService(serviceIntent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
25
app/src/main/res/drawable/logupload.xml
Normal file
25
app/src/main/res/drawable/logupload.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="100dp"
|
||||
android:height="100dp"
|
||||
android:viewportWidth="100"
|
||||
android:viewportHeight="100">
|
||||
<path
|
||||
android:pathData="M50,50m-45,0a45,45 0,1 1,90 0a45,45 0,1 1,-90 0"
|
||||
android:strokeWidth="5"
|
||||
android:fillColor="#f0f0f0"
|
||||
android:strokeColor="#bbb"/>
|
||||
<path
|
||||
android:pathData="M30,30h40v50h-40z"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#ffffff"
|
||||
android:strokeColor="#333"/>
|
||||
<path
|
||||
android:pathData="M30,30l15,0l0,15l-15,0z"
|
||||
android:fillColor="#bbb"/>
|
||||
<path
|
||||
android:pathData="M50,40l-10,10l5,0l0,10l10,0l0,-10l5,0z"
|
||||
android:fillColor="#4a90e2"/>
|
||||
<path
|
||||
android:pathData="M45,60h10v15h-10z"
|
||||
android:fillColor="#4a90e2"/>
|
||||
</vector>
|
||||
31
app/src/main/res/layout/activity_main.xml
Normal file
31
app/src/main/res/layout/activity_main.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/resultView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Looking for dumpstate..."
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/uploadButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Upload Dumpstate"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@id/resultView"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginTop="16dp"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
13
app/src/main/res/values/colors.xml
Normal file
13
app/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#6200EE</color>
|
||||
<color name="colorPrimaryVariant">#3700B3</color>
|
||||
<color name="colorSecondary">#03DAC6</color>
|
||||
<color name="colorSecondaryVariant">#018786</color>
|
||||
<color name="colorOnPrimary">#FFFFFF</color>
|
||||
<color name="colorOnSecondary">#000000</color>
|
||||
<color name="colorError">#B00020</color>
|
||||
<color name="colorOnError">#FFFFFF</color>
|
||||
<color name="colorSurface">#FFFFFF</color>
|
||||
<color name="colorBackground">#FFFFFF</color>
|
||||
</resources>
|
||||
14
app/src/main/res/values/styles.xml
Normal file
14
app/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
<resources>
|
||||
<!-- Base application theme -->
|
||||
<style name="Theme.FileUploader" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<!-- Primary branding color -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryVariant">@color/colorPrimaryVariant</item>
|
||||
<item name="colorOnPrimary">@android:color/white</item>
|
||||
<!-- Secondary branding color -->
|
||||
<item name="colorSecondary">@color/colorSecondary</item>
|
||||
<item name="colorSecondaryVariant">@color/colorSecondaryVariant</item>
|
||||
<item name="colorOnSecondary">@android:color/black</item>
|
||||
</style>
|
||||
</resources>
|
||||
12
app/src/main/res/xml/network_security_config.xml
Normal file
12
app/src/main/res/xml/network_security_config.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config cleartextTrafficPermitted="true">
|
||||
<domain includeSubdomains="true">172.16.30.69</domain> <!-- Replace with your server -->
|
||||
</domain-config>
|
||||
<!-- OR: To allow for all domains (for testing purposes only) -->
|
||||
<domain-config cleartextTrafficPermitted="true">
|
||||
<domain includeSubdomains="true">localhost</domain>
|
||||
<domain includeSubdomains="true">192.168.1.1</domain>
|
||||
<domain includeSubdomains="true">*.*</domain>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
Reference in New Issue
Block a user