mirror of
https://github.com/TimeCrafters/TimeCraftersConfigurationTool.git
synced 2025-12-15 05:02:33 +00:00
Implemented file writing, implemented write permission request, refactored Dialog to use DialogFragment, implemented json de/serializer for Settings
This commit is contained in:
39
.idea/misc.xml
generated
39
.idea/misc.xml
generated
@@ -1,5 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="NullableNotNullManager">
|
||||
<option name="myDefaultNullable" value="org.jetbrains.annotations.Nullable" />
|
||||
<option name="myDefaultNotNull" value="androidx.annotation.NonNull" />
|
||||
<option name="myNullables">
|
||||
<value>
|
||||
<list size="12">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
|
||||
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
|
||||
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
|
||||
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
|
||||
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.Nullable" />
|
||||
<item index="6" class="java.lang.String" itemvalue="android.annotation.Nullable" />
|
||||
<item index="7" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" />
|
||||
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
|
||||
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
|
||||
<item index="10" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
|
||||
<item index="11" class="java.lang.String" itemvalue="com.android.annotations.Nullable" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myNotNulls">
|
||||
<value>
|
||||
<list size="11">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
|
||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
|
||||
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
|
||||
<item index="4" class="java.lang.String" itemvalue="androidx.annotation.NonNull" />
|
||||
<item index="5" class="java.lang.String" itemvalue="android.annotation.NonNull" />
|
||||
<item index="6" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" />
|
||||
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
|
||||
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
|
||||
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
|
||||
<item index="10" class="java.lang.String" itemvalue="com.android.annotations.NonNull" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
|
||||
@@ -1,20 +1,34 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool;
|
||||
|
||||
import android.Manifest;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.Navigation;
|
||||
import androidx.navigation.ui.AppBarConfiguration;
|
||||
import androidx.navigation.ui.NavigationUI;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.Backend;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.dialogs.Dialog;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.dialogs.PermissionsRequestDialog;
|
||||
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
private static final int REQUEST_WRITE_PERMISSION = 70;
|
||||
private static final String TAG = "MainActivity";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_main);
|
||||
BottomNavigationView navView = findViewById(R.id.nav_view);
|
||||
// Passing each menu ID as a set of Ids because each
|
||||
@@ -26,6 +40,40 @@ public class MainActivity extends AppCompatActivity {
|
||||
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
|
||||
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
|
||||
NavigationUI.setupWithNavController(navView, navController);
|
||||
|
||||
if (!havePermissions()) {
|
||||
new PermissionsRequestDialog().show(getSupportFragmentManager(), null);
|
||||
} else {
|
||||
new Backend();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
|
||||
switch (requestCode) {
|
||||
case REQUEST_WRITE_PERMISSION: {
|
||||
if (grantResults.length > 0 && grantResults[0] == PERMISSION_GRANTED) {
|
||||
// Permission granted
|
||||
new Backend();
|
||||
} else {
|
||||
// Permission not given
|
||||
new PermissionsRequestDialog().show(getSupportFragmentManager(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean havePermissions() {
|
||||
return ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
public void requestStoragePermissions() {
|
||||
ActivityCompat.requestPermissions(MainActivity.this,
|
||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
REQUEST_WRITE_PERMISSION);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,30 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.backend;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.tacnet.PacketHandler;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.internal.LinkedTreeMap;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.SettingsDeserializer;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.serializers.SettingsSerializer;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class Backend {
|
||||
private static final String TAG = "Backend";
|
||||
static private Backend instance;
|
||||
private TACNET tacnet;
|
||||
private Config config;
|
||||
@@ -16,7 +35,7 @@ public class Backend {
|
||||
instance = this;
|
||||
|
||||
loadSettings();
|
||||
if (settings.config != null) {
|
||||
if (!settings.config.isEmpty()) {
|
||||
loadConfig(settings.config);
|
||||
}
|
||||
tacnet = new TACNET();
|
||||
@@ -84,28 +103,94 @@ public class Backend {
|
||||
settingsChanged = true;
|
||||
}
|
||||
|
||||
public boolean isSettingsChanged() {
|
||||
public boolean haveSettingsChanged() {
|
||||
return settingsChanged;
|
||||
}
|
||||
|
||||
public boolean loadSettings() {
|
||||
return false;
|
||||
public void loadSettings() {
|
||||
File settingsFile = new File(TAC.SETTINGS_PATH);
|
||||
|
||||
if (!settingsFile.exists()) {
|
||||
Log.i(TAG, "Writing default settings.json");
|
||||
writeDefaultSettings();
|
||||
}
|
||||
|
||||
try {
|
||||
settings = gsonForSettings().fromJson(new FileReader(settingsFile), Settings.class);
|
||||
} catch (FileNotFoundException e) {
|
||||
// TODO
|
||||
Log.e(TAG, "Unable to load settings.json");
|
||||
}
|
||||
}
|
||||
|
||||
public void saveSettings() {
|
||||
|
||||
Log.i(TAG, "Settings: " + gsonForSettings().toJson(settings));
|
||||
writeToFile(TAC.SETTINGS_PATH, gsonForSettings().toJson(settings));
|
||||
}
|
||||
|
||||
public void writeDefaultSettings() {
|
||||
/*
|
||||
{
|
||||
"data":
|
||||
{
|
||||
"hostname":TACNET.DEFAULT_HOSTNAME,
|
||||
"port":TACNET.DEFAULT_PORT,
|
||||
"config":null,
|
||||
}
|
||||
settings = new Settings(TACNET.DEFAULT_HOSTNAME, TACNET.DEFAULT_PORT, "");
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
private Gson gsonForSettings() {
|
||||
return new GsonBuilder()
|
||||
.registerTypeAdapter(Settings.class, new SettingsSerializer())
|
||||
.registerTypeAdapter(Settings.class, new SettingsDeserializer())
|
||||
.create();
|
||||
}
|
||||
|
||||
private String readFromFile(String path) {
|
||||
StringBuilder text = new StringBuilder();
|
||||
|
||||
try {
|
||||
BufferedReader br = new BufferedReader( new FileReader(path) );
|
||||
String line;
|
||||
|
||||
while((line = br.readLine()) != null) {
|
||||
text.append(line);
|
||||
text.append("\n");
|
||||
}
|
||||
|
||||
br.close();
|
||||
} catch (IOException e) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
return text.toString();
|
||||
}
|
||||
|
||||
protected boolean writeToFile(String filePath, String content) {
|
||||
try {
|
||||
if (filePath.startsWith(TAC.ROOT_PATH)) {
|
||||
createFolders(filePath);
|
||||
|
||||
FileWriter writer = new FileWriter(filePath);
|
||||
writer.write(content);
|
||||
writer.close();
|
||||
|
||||
return true;
|
||||
} else {
|
||||
Log.e(TAG, "writeToFile disallowed path: " + filePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, e.getLocalizedMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void createFolders(String filePath) throws IOException {
|
||||
File rootPath = new File(TAC.ROOT_PATH);
|
||||
File configsPath = new File(TAC.CONFIGS_PATH);
|
||||
|
||||
if (!rootPath.exists()) {
|
||||
rootPath.mkdir();
|
||||
}
|
||||
|
||||
if (!configsPath.exists()) {
|
||||
configsPath.mkdir();
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,43 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.dialogs;
|
||||
|
||||
import android.app.ActionBar;
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.os.Bundle;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.R;
|
||||
|
||||
public class Dialog extends android.app.Dialog {
|
||||
public Dialog(@NonNull Context context) {
|
||||
super(context);
|
||||
public class Dialog extends DialogFragment {
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = View.inflate(getContext(), R.layout.dialog_base, null);
|
||||
|
||||
ImageButton closeButton = v.findViewById(R.id.dialogCloseButton);
|
||||
|
||||
if (isCancelable()) {
|
||||
closeButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
closeButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
setContentView(R.layout.dialog_base);
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
ImageButton closeButton = findViewById(R.id.dialogCloseButton);
|
||||
closeButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
Point point = new Point();
|
||||
getWindow().getWindowManager().getDefaultDisplay().getSize(point);
|
||||
getWindow().setLayout((int) (point.x * 0.75), ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
getActivity().getWindowManager().getDefaultDisplay().getSize(point);
|
||||
getDialog().getWindow().setLayout((int) (point.x * 0.8), ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.dialogs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.MainActivity;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.R;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.Backend;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.TAC;
|
||||
|
||||
public class PermissionsRequestDialog extends Dialog {
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
setCancelable(false);
|
||||
|
||||
View v = super.onCreateView(inflater, container, savedInstanceState);
|
||||
|
||||
((TextView)v.findViewById(R.id.dialogTitle)).setText("Storage Permission Required");
|
||||
LinearLayout view = v.findViewById(R.id.dialogContent);
|
||||
view.addView(getLayoutInflater().inflate(R.layout.dialog_permission_request, null));
|
||||
((TextView)view.findViewById(R.id.message)).setText("Permission is required to write to external storage:\n\n" + TAC.ROOT_PATH);
|
||||
|
||||
Button quitButton = view.findViewById(R.id.quit_button);
|
||||
Button continueButton = view.findViewById(R.id.continue_button);
|
||||
|
||||
quitButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismiss();
|
||||
((MainActivity) getActivity()).close();
|
||||
}
|
||||
});
|
||||
|
||||
continueButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismiss();
|
||||
((MainActivity) getActivity()).requestStoragePermissions();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.dialogs;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.R;
|
||||
|
||||
public class VariableDialog extends Dialog {
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
setCancelable(false);
|
||||
|
||||
View v = super.onCreateView(inflater, container, savedInstanceState);
|
||||
|
||||
((TextView)v.findViewById(R.id.dialogTitle)).setText("Add Variable");
|
||||
LinearLayout view = v.findViewById(R.id.dialogContent);
|
||||
view.addView(getLayoutInflater().inflate(R.layout.dialog_edit_variable, null));
|
||||
|
||||
Button cancelButton = view.findViewById(R.id.cancel);
|
||||
Button mutateButton = view.findViewById(R.id.mutate);
|
||||
cancelButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
mutateButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.serializers;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.Settings;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class SettingsDeserializer implements JsonDeserializer<Settings> {
|
||||
@Override
|
||||
public Settings deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonObject jsonObject = json.getAsJsonObject();
|
||||
JsonObject data = jsonObject.get("data").getAsJsonObject();
|
||||
|
||||
return new Settings(
|
||||
data.get("hostname").getAsString(),
|
||||
data.get("port").getAsInt(),
|
||||
data.get("config").getAsString()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.timecrafters.TimeCraftersConfigurationTool.serializers;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.backend.Settings;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class SettingsSerializer implements JsonSerializer<Settings> {
|
||||
@Override
|
||||
public JsonElement serialize(Settings settings, Type type, JsonSerializationContext context) {
|
||||
JsonObject container = new JsonObject();
|
||||
JsonObject result = new JsonObject();
|
||||
result.add("hostname", new JsonPrimitive(settings.hostname));
|
||||
result.add("port", new JsonPrimitive(settings.port));
|
||||
result.add("config", new JsonPrimitive(settings.config));
|
||||
|
||||
container.add("data", result);
|
||||
|
||||
return container;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -15,9 +13,7 @@ import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.R;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.dialogs.Dialog;
|
||||
|
||||
import static android.view.View.inflate;
|
||||
import org.timecrafters.TimeCraftersConfigurationTool.dialogs.VariableDialog;
|
||||
|
||||
public class TACNETFragment extends Fragment {
|
||||
|
||||
@@ -38,12 +34,8 @@ public class TACNETFragment extends Fragment {
|
||||
connect.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Dialog dialog = new Dialog(getContext());
|
||||
dialog.show();
|
||||
|
||||
((TextView)dialog.findViewById(R.id.dialogTitle)).setText("Add Variable");
|
||||
LinearLayout view = dialog.findViewById(R.id.dialogContent);
|
||||
view.addView(getLayoutInflater().inflate(R.layout.dialog_edit_variable, null));
|
||||
VariableDialog dialog = new VariableDialog();
|
||||
dialog.show(getFragmentManager(), null);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -65,14 +65,14 @@
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
android:id="@+id/cancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/dialog_cancel" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button3"
|
||||
android:id="@+id/mutate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
|
||||
42
app/src/main/res/layout/dialog_permission_request.xml
Normal file
42
app/src/main/res/layout/dialog_permission_request.xml
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/message"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/quit_button"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Quit" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/continue_button"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Continue" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
Reference in New Issue
Block a user