Start background service scaffolding
This commit is contained in:
parent
2e479350de
commit
a4017c303e
36
.idea/inspectionProfiles/Project_Default.xml
Normal file
36
.idea/inspectionProfiles/Project_Default.xml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="TOP_LEVEL_CLASS_OPTIONS">
|
||||||
|
<value>
|
||||||
|
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
|
||||||
|
<option name="REQUIRED_TAGS" value="" />
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
<option name="INNER_CLASS_OPTIONS">
|
||||||
|
<value>
|
||||||
|
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
|
||||||
|
<option name="REQUIRED_TAGS" value="" />
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
<option name="METHOD_OPTIONS">
|
||||||
|
<value>
|
||||||
|
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
|
||||||
|
<option name="REQUIRED_TAGS" value="@return@param@throws or @exception" />
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
<option name="FIELD_OPTIONS">
|
||||||
|
<value>
|
||||||
|
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
|
||||||
|
<option name="REQUIRED_TAGS" value="" />
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
<option name="IGNORE_DEPRECATED" value="false" />
|
||||||
|
<option name="IGNORE_JAVADOC_PERIOD" value="true" />
|
||||||
|
<option name="IGNORE_DUPLICATED_THROWS" value="false" />
|
||||||
|
<option name="IGNORE_POINT_TO_ITSELF" value="false" />
|
||||||
|
<option name="myAdditionalJavadocTags" value="fixme" />
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.READ_SMS" />
|
||||||
|
<uses-permission android:name="android.permission.SEND_SMS" />
|
||||||
|
<uses-permission android:name="android.permission.RECEIVE_SMS" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
@ -14,7 +18,12 @@
|
|||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.StarshipHyperlink">
|
android:theme="@style/Theme.StarshipHyperlink">
|
||||||
<activity android:name=".LoginTokenFormActivity"></activity>
|
<service
|
||||||
|
android:name=".MessagingService"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="true"></service>
|
||||||
|
|
||||||
|
<activity android:name=".LoginTokenFormActivity" />
|
||||||
<activity android:name=".LoginTokenScannerActivity" />
|
<activity android:name=".LoginTokenScannerActivity" />
|
||||||
<activity android:name=".MainActivity">
|
<activity android:name=".MainActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
@ -5,6 +5,7 @@ import android.content.SharedPreferences;
|
|||||||
import com.android.volley.RequestQueue;
|
import com.android.volley.RequestQueue;
|
||||||
|
|
||||||
public class Hyperlink {
|
public class Hyperlink {
|
||||||
|
public static final int MESSAGE_TICK_POLL_INTERVAL_MS = 5000;
|
||||||
public static final String SHARED_PREFERENCES_NAME = "dev.garrettmills.starship.hyperlink.main";
|
public static final String SHARED_PREFERENCES_NAME = "dev.garrettmills.starship.hyperlink.main";
|
||||||
public static final String SERVER_ADDR = "dev.garrettmills.starship.hyperlink.server";
|
public static final String SERVER_ADDR = "dev.garrettmills.starship.hyperlink.server";
|
||||||
public static final String ACCESS_TOKEN = "dev.garrettmills.starship.hyperlink.token.server";
|
public static final String ACCESS_TOKEN = "dev.garrettmills.starship.hyperlink.token.server";
|
||||||
@ -13,6 +14,10 @@ public class Hyperlink {
|
|||||||
|
|
||||||
public static final int REQUEST_LOGIN_TOKEN = 180;
|
public static final int REQUEST_LOGIN_TOKEN = 180;
|
||||||
public static final int REQUEST_PERMISSION_CAMERA = 181;
|
public static final int REQUEST_PERMISSION_CAMERA = 181;
|
||||||
|
public static final int REQUEST_PERMISSION_READ_SMS = 182;
|
||||||
|
|
||||||
|
public static final String NOTIFICATION_CHANNEL = "dev.garrettmills.starship.hyperlink.notifications";
|
||||||
|
public static final int SERVICE_NOTIFICATION_ID = 183;
|
||||||
|
|
||||||
public static SharedPreferences preferences;
|
public static SharedPreferences preferences;
|
||||||
public static RequestQueue httpRequestQueue;
|
public static RequestQueue httpRequestQueue;
|
||||||
|
@ -1,22 +1,49 @@
|
|||||||
package dev.garrettmills.starship.hyperlink;
|
package dev.garrettmills.starship.hyperlink;
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import android.Manifest;
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.database.Cursor;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.provider.Telephony;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import com.android.volley.toolbox.Volley;
|
import com.android.volley.toolbox.Volley;
|
||||||
|
|
||||||
|
import dev.garrettmills.starship.hyperlink.util.APIv1;
|
||||||
|
import dev.garrettmills.starship.hyperlink.util.AccessToken;
|
||||||
|
import dev.garrettmills.starship.hyperlink.util.InvalidLoginTokenException;
|
||||||
|
import dev.garrettmills.starship.hyperlink.util.LoginToken;
|
||||||
|
import dev.garrettmills.starship.hyperlink.util.NotAuthenticatedException;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
TextView serverAddressView;
|
||||||
|
Intent serviceIntent = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
Hyperlink.preferences = getSharedPreferences(Hyperlink.SHARED_PREFERENCES_NAME, MODE_PRIVATE);
|
Hyperlink.preferences = getSharedPreferences(Hyperlink.SHARED_PREFERENCES_NAME, MODE_PRIVATE);
|
||||||
Hyperlink.httpRequestQueue = Volley.newRequestQueue(this);
|
Hyperlink.httpRequestQueue = Volley.newRequestQueue(this);
|
||||||
|
|
||||||
setContentView(R.layout.activity_main);
|
NotificationChannel channel = new NotificationChannel(Hyperlink.NOTIFICATION_CHANNEL, "Starship Hyperlink", NotificationManager.IMPORTANCE_LOW);
|
||||||
|
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
assert manager != null;
|
||||||
|
manager.createNotificationChannel(channel);
|
||||||
|
|
||||||
|
if (APIv1.isAuthenticated()) switchToStatusMode();
|
||||||
|
else switchToLoginMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onScanTokenClick(View view) {
|
public void onScanTokenClick(View view) {
|
||||||
@ -29,6 +56,76 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
startActivityForResult(intent, Hyperlink.REQUEST_LOGIN_TOKEN);
|
startActivityForResult(intent, Hyperlink.REQUEST_LOGIN_TOKEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onLogoutButtonClick(View view) {
|
||||||
|
switchToLoginMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void switchToStatusMode() {
|
||||||
|
setContentView(R.layout.activity_main_status);
|
||||||
|
serverAddressView = findViewById(R.id.activity_main_serverAddressTextView);
|
||||||
|
|
||||||
|
try {
|
||||||
|
AccessToken token = APIv1.getToken();
|
||||||
|
serverAddressView.setText(token.getServer());
|
||||||
|
} catch (NotAuthenticatedException e) {
|
||||||
|
if ( BuildConfig.DEBUG ) {
|
||||||
|
Toast.makeText(getApplicationContext(), "Cannot fetch token; not authenticated.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
APIv1.logout();
|
||||||
|
switchToLoginMode();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestSMSRead();
|
||||||
|
Cursor cursor = getContentResolver().query(Telephony.Threads.CONTENT_URI, null, null, null, "date DESC");
|
||||||
|
if ( cursor.moveToFirst() ) {
|
||||||
|
int j = 0;
|
||||||
|
do {
|
||||||
|
j += 1;
|
||||||
|
if ( j > 5 ) break;
|
||||||
|
for ( int i = 0; i < cursor.getColumnCount(); i += 1 ) {
|
||||||
|
Log.i("MainActivity", cursor.getColumnName(i) + ": " + cursor.getString(i));
|
||||||
|
}
|
||||||
|
} while ( cursor.moveToNext() );
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceIntent = new Intent(this, MessagingService.class);
|
||||||
|
ContextCompat.startForegroundService(this, serviceIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void switchToLoginMode() {
|
||||||
|
APIv1.logout();
|
||||||
|
|
||||||
|
if ( serviceIntent != null ) stopService(serviceIntent);
|
||||||
|
|
||||||
|
setContentView(R.layout.activity_main);
|
||||||
|
serverAddressView = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void requestSMSRead() {
|
||||||
|
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_SMS) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
// switchToStatusMode();
|
||||||
|
} else {
|
||||||
|
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_SMS}, Hyperlink.REQUEST_PERMISSION_READ_SMS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
|
if (requestCode == Hyperlink.REQUEST_PERMISSION_READ_SMS) {
|
||||||
|
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
switchToStatusMode();
|
||||||
|
} else {
|
||||||
|
if ( BuildConfig.DEBUG ) {
|
||||||
|
Toast.makeText(this, "Read SMS permission denied", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
switchToLoginMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
@ -38,6 +135,15 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
if ( BuildConfig.DEBUG ) {
|
if ( BuildConfig.DEBUG ) {
|
||||||
Toast.makeText(getApplicationContext(), data.getExtras().getString(Hyperlink.EXTRA_LOGIN_TOKEN), Toast.LENGTH_SHORT).show();
|
Toast.makeText(getApplicationContext(), data.getExtras().getString(Hyperlink.EXTRA_LOGIN_TOKEN), Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
LoginToken login = new LoginToken(data.getExtras().getString(Hyperlink.EXTRA_LOGIN_TOKEN));
|
||||||
|
APIv1.login(login);
|
||||||
|
switchToStatusMode();
|
||||||
|
} catch (InvalidLoginTokenException e) {
|
||||||
|
Toast.makeText(getApplicationContext(), "Invalid login token!", Toast.LENGTH_SHORT).show();
|
||||||
|
switchToLoginMode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,76 @@
|
|||||||
|
package dev.garrettmills.starship.hyperlink;
|
||||||
|
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import java.sql.Time;
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
|
||||||
|
import dev.garrettmills.starship.hyperlink.relay.ServerSentRequest;
|
||||||
|
|
||||||
|
public class MessagingService extends Service {
|
||||||
|
private final Handler handler = new Handler();
|
||||||
|
private final Timer timer = new Timer();
|
||||||
|
|
||||||
|
public MessagingService() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
Intent notificationIntent = new Intent(this, MainActivity.class);
|
||||||
|
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
|
||||||
|
|
||||||
|
Notification notification = new Notification.Builder(this, Hyperlink.NOTIFICATION_CHANNEL)
|
||||||
|
.setContentTitle("Starship Hyperlink is Running")
|
||||||
|
.setContentText("Your text messages are being relayed to Hyperlink.")
|
||||||
|
.setSmallIcon(R.drawable.ic_launcher_foreground)
|
||||||
|
.setContentIntent(pendingIntent)
|
||||||
|
.setTicker("ticker")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
startForeground(Hyperlink.SERVICE_NOTIFICATION_ID, notification);
|
||||||
|
startConnection();
|
||||||
|
return START_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
timer.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startConnection() {
|
||||||
|
TimerTask task = new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
handler.post(() -> {
|
||||||
|
if ( !tick() ) {
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
timer.schedule(task, 0, Hyperlink.MESSAGE_TICK_POLL_INTERVAL_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tick() {
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleServerSentRequest(ServerSentRequest request) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package dev.garrettmills.starship.hyperlink.relay;
|
||||||
|
|
||||||
|
public enum ServerRequestEndpoint {
|
||||||
|
LIST_THREADS
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package dev.garrettmills.starship.hyperlink.relay;
|
||||||
|
|
||||||
|
public class ServerSentRequest {
|
||||||
|
private String _uuid;
|
||||||
|
private ServerRequestEndpoint _endpoint;
|
||||||
|
|
||||||
|
public String getUUID() {
|
||||||
|
return _uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerRequestEndpoint getEndpoint() {
|
||||||
|
return _endpoint;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
package dev.garrettmills.starship.hyperlink.util;
|
package dev.garrettmills.starship.hyperlink.util;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
import dev.garrettmills.starship.hyperlink.Hyperlink;
|
import dev.garrettmills.starship.hyperlink.Hyperlink;
|
||||||
|
|
||||||
public class APIv1 {
|
public class APIv1 {
|
||||||
@ -22,4 +24,45 @@ public class APIv1 {
|
|||||||
|
|
||||||
return server + "/api/v1" + endpoint;
|
return server + "/api/v1" + endpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isAuthenticated() {
|
||||||
|
return !Hyperlink.preferences.getString(Hyperlink.SERVER_ADDR, "").equals("")
|
||||||
|
&& !Hyperlink.preferences.getString(Hyperlink.ACCESS_TOKEN, "").equals("");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AccessToken getToken() throws NotAuthenticatedException {
|
||||||
|
if ( !isAuthenticated() ) {
|
||||||
|
throw new NotAuthenticatedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AccessToken(
|
||||||
|
Hyperlink.preferences.getString(Hyperlink.SERVER_ADDR, ""),
|
||||||
|
Hyperlink.preferences.getString(Hyperlink.ACCESS_TOKEN, "")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void logout() {
|
||||||
|
SharedPreferences.Editor editor = Hyperlink.preferences.edit();
|
||||||
|
editor.putString(Hyperlink.SERVER_ADDR, "");
|
||||||
|
editor.putString(Hyperlink.ACCESS_TOKEN, "");
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AccessToken login(LoginToken login) {
|
||||||
|
AccessToken token = redeemLoginToken(login);
|
||||||
|
SharedPreferences.Editor editor = Hyperlink.preferences.edit();
|
||||||
|
editor.putString(Hyperlink.SERVER_ADDR, token.getServer());
|
||||||
|
editor.putString(Hyperlink.ACCESS_TOKEN, token.getToken());
|
||||||
|
editor.apply();
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fixme this is a stub placeholder. Replace with actual implementation
|
||||||
|
* @param login the LoginToken from the user
|
||||||
|
* @return the AccessToken redeemed from the server
|
||||||
|
*/
|
||||||
|
protected static AccessToken redeemLoginToken(LoginToken login) {
|
||||||
|
return new AccessToken(login.getServer(), login.getToken());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,21 @@
|
|||||||
package dev.garrettmills.starship.hyperlink.util;
|
package dev.garrettmills.starship.hyperlink.util;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
public class AccessToken {
|
public class AccessToken {
|
||||||
|
private final String _server;
|
||||||
|
private final String _token;
|
||||||
|
|
||||||
|
public AccessToken(@NonNull String server, @NonNull String token) {
|
||||||
|
_server = server;
|
||||||
|
_token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServer() {
|
||||||
|
return _server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getToken() {
|
||||||
|
return _token;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
package dev.garrettmills.starship.hyperlink.util;
|
||||||
|
|
||||||
|
public class NotAuthenticatedException extends Exception {
|
||||||
|
}
|
44
app/src/main/res/layout/activity_main_status.xml
Normal file
44
app/src/main/res/layout/activity_main_status.xml
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
tools:context=".MainActivity">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/activity_main_logoutButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:onClick="onLogoutButtonClick"
|
||||||
|
android:text="Log Out"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.498"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_bias="0.606" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="140dp"
|
||||||
|
android:text="Connected to Hyperlink"
|
||||||
|
android:textColor="#4CAF50"
|
||||||
|
android:textSize="20sp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/activity_main_logoutButton"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/activity_main_serverAddressTextView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="18sp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/activity_main_logoutButton"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/textView2" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in New Issue
Block a user