Compare commits

..

2 Commits

20 changed files with 736 additions and 316 deletions

BIN
ProbnaSlika.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 MiB

View File

@ -40,6 +40,8 @@ dependencies {
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation ("com.github.bumptech.glide:glide:4.16.0")
annotationProcessor ("com.github.bumptech.glide:compiler:4.16.0")
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)

View File

@ -16,7 +16,8 @@
<activity
android:name=".NewPostActivity"
android:exported="false"
tools:ignore="Instantiatable" />
tools:ignore="Instantiatable"
android:windowSoftInputMode="adjustResize|stateHidden"/>
<activity
android:name=".RegisterActivity"
android:exported="false" />

View File

@ -4,6 +4,7 @@ import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
@ -12,7 +13,6 @@ import com.example.gallery.api.ApiClient;
import com.example.gallery.api.ApiService;
import com.example.gallery.models.LoginRequest;
import com.example.gallery.models.LoginResponse;
import com.example.gallery.UserResponse;
import retrofit2.Call;
import retrofit2.Callback;
@ -20,84 +20,60 @@ import retrofit2.Response;
public class LoginActivity extends AppCompatActivity {
EditText usernameEdit, passwordEdit;
Button loginBtn;
private EditText usernameEdit, passwordEdit;
private Button loginBtn;
private TextView registerTV;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
// Updated ids from your XML
usernameEdit = findViewById(R.id.usernameTxt);
passwordEdit = findViewById(R.id.userpasswordTxt);
loginBtn = findViewById(R.id.btnLogin);
registerTV = findViewById(R.id.registerTV);
loginBtn.setOnClickListener(v -> {
String username = usernameEdit.getText().toString().trim();
String password = passwordEdit.getText().toString().trim();
loginBtn.setOnClickListener(v -> login());
}
if (username.isEmpty() || password.isEmpty()) {
Toast.makeText(LoginActivity.this, "All fields are required", Toast.LENGTH_SHORT).show();
return;
private void login() {
String username = usernameEdit.getText().toString().trim();
String password = passwordEdit.getText().toString().trim();
if (username.isEmpty() || password.isEmpty()) {
Toast.makeText(this, "All fields are required", Toast.LENGTH_SHORT).show();
return;
}
ApiService apiService = ApiClient.getClient().create(ApiService.class);
LoginRequest request = new LoginRequest(username, password);
apiService.login(request).enqueue(new Callback<LoginResponse>() {
@Override
public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
if (response.isSuccessful() && response.body() != null) {
// 🔑 STORE RAW TOKEN ONLY
getSharedPreferences("MyAppPrefs", MODE_PRIVATE)
.edit()
.putString("authToken", response.body().getToken())
.putBoolean("isLoggedIn", true)
.apply();
Toast.makeText(LoginActivity.this, "Login successful", Toast.LENGTH_SHORT).show();
startActivity(new Intent(LoginActivity.this, MainActivity.class));
finish();
} else {
Toast.makeText(LoginActivity.this, "Login failed", Toast.LENGTH_SHORT).show();
}
}
ApiService apiService = ApiClient.getClient().create(ApiService.class);
LoginRequest request = new LoginRequest(username, password);
// 🔹 Login API call
apiService.login(request).enqueue(new Callback<LoginResponse>() {
@Override
public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
if (response.isSuccessful() && response.body() != null) {
String token = "Token " + response.body().getToken();
// 🔹 Save token & login state in SharedPreferences
getSharedPreferences("MyAppPrefs", MODE_PRIVATE)
.edit()
.putString("authToken", token)
.putBoolean("isLoggedIn", true)
.apply();
Toast.makeText(LoginActivity.this, "Login successful", Toast.LENGTH_SHORT).show();
// 🔹 Verify token with /api/auth/me/
apiService.getMe(token).enqueue(new Callback<UserResponse>() {
@Override
public void onResponse(Call<UserResponse> call, Response<UserResponse> response) {
if (response.isSuccessful() && response.body() != null) {
Toast.makeText(LoginActivity.this,
"Welcome " + response.body().getUsername(),
Toast.LENGTH_SHORT).show();
// Go to MainActivity
startActivity(new Intent(LoginActivity.this, MainActivity.class));
finish();
} else {
Toast.makeText(LoginActivity.this,
"Failed to verify user",
Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<UserResponse> call, Throwable t) {
Toast.makeText(LoginActivity.this,
"Verification error: " + t.getMessage(),
Toast.LENGTH_SHORT).show();
}
});
} else {
Toast.makeText(LoginActivity.this, "Login failed", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<LoginResponse> call, Throwable t) {
Toast.makeText(LoginActivity.this, "Server error: " + t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
@Override
public void onFailure(Call<LoginResponse> call, Throwable t) {
Toast.makeText(LoginActivity.this, "Server error: " + t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
}

View File

@ -4,62 +4,135 @@ import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.example.gallery.adapters.PostsAdapter;
import com.example.gallery.api.ApiClient;
import com.example.gallery.api.ApiService;
import com.example.gallery.models.Publication;
import com.example.gallery.models.PublicationListResponse;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class MainActivity extends AppCompatActivity {
Button loginBtn, registerBtn, logoutBtn;
private Button loginBtn, registerBtn, logoutBtn, newPostBtn;
private RecyclerView recyclerView;
private PostsAdapter postsAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
// Initialize all views BEFORE using them
loginBtn = findViewById(R.id.Loginbtn);
registerBtn = findViewById(R.id.Registerbtn);
logoutBtn = findViewById(R.id.btnLogout);
newPostBtn = findViewById(R.id.newPostBtn);
recyclerView = findViewById(R.id.postsRecyclerView);
// Button listeners
loginBtn.setOnClickListener(v -> {
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
startActivity(intent);
});
// RecyclerView setup
recyclerView.setLayoutManager(new LinearLayoutManager(this));
postsAdapter = new PostsAdapter(new ArrayList<>());
recyclerView.setAdapter(postsAdapter);
registerBtn.setOnClickListener(v -> {
Intent intent = new Intent(MainActivity.this, RegisterActivity.class);
startActivity(intent);
});
setupButtons();
updateAuthUI();
loadPublications();
}
// Check login state from SharedPreferences
private void setupButtons() {
if (loginBtn != null) {
loginBtn.setOnClickListener(v ->
startActivity(new Intent(MainActivity.this, LoginActivity.class))
);
}
if (registerBtn != null) {
registerBtn.setOnClickListener(v ->
startActivity(new Intent(MainActivity.this, RegisterActivity.class))
);
}
if (logoutBtn != null) {
logoutBtn.setOnClickListener(v -> {
getSharedPreferences("MyAppPrefs", MODE_PRIVATE).edit().clear().apply();
updateAuthUI();
Toast.makeText(MainActivity.this, "Logged out", Toast.LENGTH_SHORT).show();
});
}
if (newPostBtn != null) {
newPostBtn.setOnClickListener(v ->
startActivityForResult(
new Intent(MainActivity.this, NewPostActivity.class), 100)
);
}
}
private void updateAuthUI() {
boolean isLoggedIn = getSharedPreferences("MyAppPrefs", MODE_PRIVATE)
.getBoolean("isLoggedIn", false);
if (isLoggedIn) {
// User is logged in hide login/register, show logout
loginBtn.setVisibility(View.GONE);
registerBtn.setVisibility(View.GONE);
logoutBtn.setVisibility(View.VISIBLE);
} else {
// User not logged in show login/register, hide logout
loginBtn.setVisibility(View.VISIBLE);
registerBtn.setVisibility(View.VISIBLE);
logoutBtn.setVisibility(View.GONE);
}
if (loginBtn != null) loginBtn.setVisibility(isLoggedIn ? View.GONE : View.VISIBLE);
if (registerBtn != null) registerBtn.setVisibility(isLoggedIn ? View.GONE : View.VISIBLE);
if (logoutBtn != null) logoutBtn.setVisibility(isLoggedIn ? View.VISIBLE : View.GONE);
if (newPostBtn != null) newPostBtn.setVisibility(isLoggedIn ? View.VISIBLE : View.GONE);
}
// Logout button
logoutBtn.setOnClickListener(v -> {
// Clear token and login state
getSharedPreferences("MyAppPrefs", MODE_PRIVATE)
.edit()
.remove("authToken") // remove saved token
.putBoolean("isLoggedIn", false) // set logged out
.apply();
private void loadPublications() {
ApiService apiService = ApiClient.getClient().create(ApiService.class);
// Refresh activity to reflect state change
recreate();
String token = getSharedPreferences("MyAppPrefs", MODE_PRIVATE)
.getString("authToken", null);
String authHeader = token != null ? "Token " + token : null;
Call<PublicationListResponse> call = apiService.getPublications(authHeader);
call.enqueue(new Callback<PublicationListResponse>() {
@Override
public void onResponse(Call<PublicationListResponse> call, Response<PublicationListResponse> response) {
if (response.isSuccessful() && response.body() != null) {
List<Publication> posts = response.body().results;
Toast.makeText(MainActivity.this,
"Posts loaded: " + posts.size(),
Toast.LENGTH_SHORT).show();
postsAdapter.updateData(posts);
} else {
Toast.makeText(MainActivity.this,
"Failed to load publications: " + response.code(), Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<PublicationListResponse> call, Throwable t) {
Toast.makeText(MainActivity.this,
"Failed to load publications: " + t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 100 && resultCode == RESULT_OK) {
loadPublications(); // reload feed after new post
}
}
@Override
protected void onResume() {
super.onResume();
updateAuthUI();
loadPublications();
}
}

View File

@ -1,4 +1,175 @@
package com.example.gallery;
public class NewPostActivity {
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.widget.*;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import com.example.gallery.api.ApiClient;
import com.example.gallery.api.ApiService;
import com.example.gallery.models.FileUtils;
import com.example.gallery.models.Publication;
import java.io.File;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class NewPostActivity extends AppCompatActivity {
private EditText descriptionET;
private RadioGroup mediaTypeRG;
private Spinner categorySpinner;
private Button selectMediaBtn, submitBtn, cancelBtn;
private TextView selectedFileTV;
private ImageView previewIV;
private Uri selectedFileUri;
private String mediaType = "image";
private int selectedCategory = 1; // default
private final ActivityResultLauncher<Intent> picker =
registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
selectedFileUri = result.getData().getData();
selectedFileTV.setText(FileUtils.getFileName(this, selectedFileUri));
if (mediaType.equals("image")) {
previewIV.setImageURI(selectedFileUri);
previewIV.setVisibility(ImageView.VISIBLE);
} else {
previewIV.setVisibility(ImageView.GONE);
}
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_new_post);
descriptionET = findViewById(R.id.descriptionET);
mediaTypeRG = findViewById(R.id.mediaTypeRG);
selectMediaBtn = findViewById(R.id.selectMediaBtn);
submitBtn = findViewById(R.id.submitBtn);
cancelBtn = findViewById(R.id.cancelBtn);
selectedFileTV = findViewById(R.id.selectedFileTV);
previewIV = findViewById(R.id.previewIV);
// Spinner for categories
categorySpinner = findViewById(R.id.categorySpinner);
ArrayAdapter<CharSequence> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_spinner_item,
new String[]{"nature","city","landscape","animals","people"}); // categories from API
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
categorySpinner.setAdapter(adapter);
categorySpinner.setSelection(0);
categorySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override public void onItemSelected(AdapterView<?> parent, android.view.View view, int position, long id) {
selectedCategory = new int[]{1,2,3,4,5}[position];
}
@Override public void onNothingSelected(AdapterView<?> parent) {}
});
mediaTypeRG.setOnCheckedChangeListener((g, id) -> mediaType = (id == R.id.photoRB) ? "image" : "video");
selectMediaBtn.setOnClickListener(v -> {
Intent i = new Intent(Intent.ACTION_PICK);
i.setType(mediaType.equals("image") ? "image/*" : "video/*");
picker.launch(i);
});
submitBtn.setOnClickListener(v -> uploadPost());
cancelBtn.setOnClickListener(v -> confirmCancel());
}
@Override
public void onBackPressed() { confirmCancel(); }
private void confirmCancel() {
new AlertDialog.Builder(this)
.setTitle("Cancel post")
.setMessage("Are you sure?")
.setPositiveButton("Yes", (d, w) -> super.onBackPressed())
.setNegativeButton("No", null)
.show();
}
private void uploadPost() {
if (selectedFileUri == null || descriptionET.getText().toString().trim().isEmpty()) {
Toast.makeText(this, "Select file and enter description", Toast.LENGTH_SHORT).show();
return;
}
String rawToken = getSharedPreferences("MyAppPrefs", MODE_PRIVATE)
.getString("authToken", null);
if (rawToken == null) {
Toast.makeText(this, "Login required", Toast.LENGTH_SHORT).show();
return;
}
String authHeader = "Token " + rawToken;
try {
File file = FileUtils.getFileFromUri(this, selectedFileUri);
MediaType mediaTypeFile = MediaType.parse(getContentResolver().getType(selectedFileUri));
RequestBody fileBody = RequestBody.create(mediaTypeFile, file);
MultipartBody.Part filePart = MultipartBody.Part.createFormData(
mediaType.equals("image") ? "image" : "video",
file.getName(),
fileBody
);
RequestBody desc = RequestBody.create(MediaType.parse("text/plain"),
descriptionET.getText().toString().trim());
RequestBody ct = RequestBody.create(MediaType.parse("text/plain"), mediaType);
RequestBody status = RequestBody.create(MediaType.parse("text/plain"), "public");
RequestBody category = RequestBody.create(MediaType.parse("text/plain"), String.valueOf(selectedCategory));
ApiService apiService = ApiClient.getClient().create(ApiService.class);
Call<Publication> call = apiService.createPublication(
authHeader,
desc,
ct,
status,
category,
filePart
);
call.enqueue(new Callback<Publication>() {
@Override
public void onResponse(Call<Publication> call, Response<Publication> response) {
if (response.isSuccessful() && response.body() != null) {
Toast.makeText(NewPostActivity.this, "Post created 🎉", Toast.LENGTH_SHORT).show();
setResult(RESULT_OK); // signal MainActivity to refresh
finish();
} else {
Toast.makeText(NewPostActivity.this,
"Error " + response.code() + ": check fields", Toast.LENGTH_LONG).show();
}
}
@Override
public void onFailure(Call<Publication> call, Throwable t) {
Toast.makeText(NewPostActivity.this, t.getMessage(), Toast.LENGTH_LONG).show();
}
});
} catch (Exception e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
}
}
}

View File

@ -0,0 +1,104 @@
package com.example.gallery.adapters;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.example.gallery.R;
import com.example.gallery.api.ApiClient;
import com.example.gallery.models.Publication;
import java.util.List;
public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.PostViewHolder> {
private final List<Publication> publications;
public PostsAdapter(List<Publication> publications) {
this.publications = publications;
}
public void updateData(List<Publication> newData) {
publications.clear();
publications.addAll(newData);
notifyDataSetChanged();
}
@NonNull
@Override
public PostViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_post, parent, false);
return new PostViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull PostViewHolder holder, int position) {
Publication pub = publications.get(position);
holder.descriptionTV.setText(
pub.description != null ? pub.description : ""
);
holder.mediaTypeBadge.setText(
pub.content_type != null ? pub.content_type.toUpperCase() : ""
);
String mediaPath = null;
if ("image".equals(pub.content_type)) {
mediaPath = pub.image;
} else if ("video".equals(pub.content_type)) {
mediaPath = pub.video;
}
if (mediaPath != null && !mediaPath.isEmpty()) {
String fullUrl;
// CRITICAL FIX: handle both relative & absolute URLs
if (mediaPath.startsWith("http")) {
fullUrl = mediaPath;
} else {
fullUrl = ApiClient.BASE_URL + mediaPath;
}
Log.d("POST_IMAGE_URL", fullUrl);
Glide.with(holder.itemView.getContext())
.load(fullUrl)
.placeholder(R.drawable.image_placeholder_background)
.error(R.drawable.image_placeholder_background)
.centerCrop()
.into(holder.mediaIV);
} else {
holder.mediaIV.setImageResource(R.drawable.image_placeholder_background);
}
}
@Override
public int getItemCount() {
return publications.size();
}
static class PostViewHolder extends RecyclerView.ViewHolder {
TextView descriptionTV;
TextView mediaTypeBadge;
ImageView mediaIV;
public PostViewHolder(@NonNull View itemView) {
super(itemView);
descriptionTV = itemView.findViewById(R.id.postDescription);
mediaIV = itemView.findViewById(R.id.postImage);
mediaTypeBadge = itemView.findViewById(R.id.mediaTypeBadge);
}
}
}

View File

@ -5,10 +5,12 @@ import retrofit2.converter.gson.GsonConverterFactory;
public class ApiClient {
private static final String BASE_URL = "https://gallery.steve-dekart.xyz/";
// Base URL of your backend
public static final String BASE_URL = "https://gallery.steve-dekart.xyz/";
private static Retrofit retrofit;
// Returns singleton Retrofit client
public static Retrofit getClient() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
@ -18,4 +20,4 @@ public class ApiClient {
}
return retrofit;
}
}
}

View File

@ -3,17 +3,25 @@ package com.example.gallery.api;
import com.example.gallery.UserResponse;
import com.example.gallery.models.LoginRequest;
import com.example.gallery.models.LoginResponse;
import com.example.gallery.models.Publication;
import com.example.gallery.models.PublicationListResponse;
import com.example.gallery.models.RegisterRequest;
import com.example.gallery.models.RegisterResponse;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
public interface ApiService {
/* ===================== AUTH ===================== */
@POST("api/auth/register/")
Call<RegisterResponse> register(@Body RegisterRequest request);
@ -22,4 +30,22 @@ public interface ApiService {
@GET("api/auth/me/")
Call<UserResponse> getMe(@Header("Authorization") String token);
}
/* ================== PUBLICATIONS ================= */
@GET("api/publications/")
Call<PublicationListResponse> getPublications(
@Header("Authorization") String token
);
@Multipart
@POST("api/publications/")
Call<Publication> createPublication(
@Header("Authorization") String authHeader,
@Part("description") RequestBody description,
@Part("content_type") RequestBody contentType,
@Part("status") RequestBody status,
@Part("category") RequestBody category,
@Part MultipartBody.Part file
);
}

View File

@ -0,0 +1,48 @@
package com.example.gallery.models;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.OpenableColumns;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
public class FileUtils {
public static File getFileFromUri(Context context, Uri uri) throws Exception {
String fileName = getFileName(context, uri);
File tempFile = new File(context.getCacheDir(), fileName);
tempFile.createNewFile();
try (InputStream inputStream = context.getContentResolver().openInputStream(uri);
FileOutputStream outputStream = new FileOutputStream(tempFile)) {
byte[] buf = new byte[1024];
int len;
while ((len = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, len);
}
}
return tempFile;
}
public static String getFileName(Context context, Uri uri) {
String result = null;
if ("content".equals(uri.getScheme())) {
try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
if (nameIndex >= 0) {
result = cursor.getString(nameIndex);
}
}
}
}
if (result == null) {
result = uri.getLastPathSegment();
}
return result;
}
}

View File

@ -0,0 +1,16 @@
package com.example.gallery.models;
public class Post {
private int id;
private String description;
private String media_type;
private String file_url;
private String username;
// getters
public int getId() { return id; }
public String getDescription() { return description; }
public String getMedia_type() { return media_type; }
public String getFile_url() { return file_url; }
public String getUsername() { return username; }
}

View File

@ -0,0 +1,11 @@
package com.example.gallery.models;
public class PostResponse {
private boolean success;
private String message;
private Post post;
public boolean isSuccess() { return success; }
public String getMessage() { return message; }
public Post getPost() { return post; }
}

View File

@ -0,0 +1,11 @@
package com.example.gallery.models;
public class Publication {
public int pk;
public String image;
public String video;
public String content_type;
public String description;
public String time_created;
}

View File

@ -0,0 +1,11 @@
package com.example.gallery.models;
import java.util.List;
public class PublicationListResponse {
public int count;
public int page;
public int page_size;
public int total_pages;
public List<Publication> results;
}

View File

@ -0,0 +1,9 @@
package com.example.gallery.models;
public class PublicationRequest {
public String image;
public String video;
public String content_type;
public String description;
public int category;
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
</selector>

View File

@ -97,14 +97,14 @@
<!-- Register Text -->
<TextView
android:id="@+id/registerTV"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Don't have an account? Register"
android:textSize="14sp"
android:textColor="#03396c"
android:gravity="center"
android:layout_width="281dp"
android:layout_height="51dp"
android:clickable="true"
android:focusable="true"/>
android:focusable="true"
android:gravity="center"
android:text="Don't have an account? Register"
android:textColor="#03396c"
android:textSize="14sp" />
</LinearLayout>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
@ -8,116 +9,69 @@
android:background="@color/bgColor"
tools:context=".MainActivity">
<androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Toolbar -->
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="0dp"
android:layout_height="?attr/actionBarSize"
android:background="@color/header"
app:title="Gallery"
app:titleTextColor="#011f4b"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<!-- Main content -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/header"
android:elevation="4dp"
app:title="My App"
app:titleTextColor="#011f4b">
<!-- Custom Toolbar Content -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="center_vertical">
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1"/>
<!-- Filter Dropdown Spinner -->
<Spinner
android:id="@+id/filterButton"
android:layout_width="120dp"
android:layout_height="40dp"
android:background="@drawable/button_background"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:layout_marginEnd="16dp"
android:spinnerMode="dropdown"
android:popupBackground="#b3cde0"/>
<!-- App Logo -->
<ImageView
android:id="@+id/appLogo"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@color/header"
android:layout_marginEnd="16dp"
android:contentDescription="App Logo"/>
</LinearLayout>
</androidx.appcompat.widget.Toolbar>
<!-- Feed/Posts RecyclerView -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/postsRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:clipToPadding="false"/>
</LinearLayout>
<!-- Left sliding menu -->
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="280dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="@color/bgColor"
app:headerLayout="@layout/nav_header"
app:menu="@menu/drawer_menu" />
</androidx.drawerlayout.widget.DrawerLayout>
<!-- RecyclerView Feed -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/postsRecyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="16dp"
app:layout_constraintTop_toBottomOf="@id/toolbar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:contentDescription="Enter How Much Cookies You Want"/>
<!-- Login Button -->
<Button
android:id="@+id/Loginbtn"
android:layout_width="103dp"
android:layout_height="51dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Log in"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
android:layout_margin="16dp"
app:layout_constraintTop_toTopOf="@id/toolbar"
app:layout_constraintStart_toStartOf="parent"/>
<!-- Register Button -->
<Button
android:id="@+id/Registerbtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="76dp"
android:text="Register"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
android:layout_marginStart="16dp"
app:layout_constraintTop_toBottomOf="@id/Loginbtn"
app:layout_constraintStart_toStartOf="parent"/>
<!-- Logout Button -->
<Button
android:id="@+id/btnLogout"
android:layout_width="101dp"
android:layout_height="58dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Logout"
android:visibility="invisible"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
android:layout_margin="16dp"
android:visibility="gone"
app:layout_constraintTop_toTopOf="@id/toolbar"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- New Post Button -->
<Button
android:id="@+id/newPostBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New Post"
android:layout_margin="16dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -6,125 +6,126 @@
android:layout_height="match_parent"
tools:context=".NewPostActivity">
<LinearLayout
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
android:layout_height="match_parent">
<TextView
android:id="@+id/headerTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Create New Post"
android:textSize="24sp"
android:layout_marginBottom="24dp"/>
<TextView
android:id="@+id/titleLabelTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title"
android:textSize="16sp"/>
<EditText
android:id="@+id/titleET"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter post title"
android:inputType="text"
android:layout_marginBottom="16dp"/>
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/descriptionLabelTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Description"
android:textSize="16sp"/>
<EditText
android:id="@+id/descriptionET"
android:layout_width="match_parent"
android:layout_height="120dp"
android:hint="Enter post description"
android:inputType="textMultiLine"
android:gravity="top"
android:layout_marginBottom="16dp"/>
<TextView
android:id="@+id/mediaTypeLabelTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Media Type"
android:textSize="16sp"/>
<RadioGroup
android:id="@+id/mediaTypeRG"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="16dp">
<RadioButton
android:id="@+id/photoRB"
<TextView
android:id="@+id/headerTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Photo"
android:checked="true"/>
android:text="Create New Post"
android:textSize="24sp"
android:layout_marginBottom="24dp"/>
<RadioButton
android:id="@+id/videoRB"
<TextView
android:id="@+id/descriptionLabelTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Video"
android:layout_marginStart="24dp"/>
android:text="Description"
android:textSize="16sp"/>
</RadioGroup>
<EditText
android:id="@+id/descriptionET"
android:layout_width="match_parent"
android:layout_height="120dp"
android:hint="Enter post description"
android:inputType="textMultiLine"
android:gravity="top"
android:layout_marginBottom="16dp"/>
<TextView
android:id="@+id/selectMediaLabelTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Select Media"
android:textSize="16sp"/>
<TextView
android:id="@+id/mediaTypeLabelTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Media Type"
android:textSize="16sp"/>
<Button
android:id="@+id/selectMediaBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Choose File"
android:layout_marginBottom="16dp"/>
<RadioGroup
android:id="@+id/mediaTypeRG"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="16dp">
<TextView
android:id="@+id/selectedFileTV"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="No file selected"
android:textSize="14sp"
android:layout_marginBottom="16dp"/>
<RadioButton
android:id="@+id/photoRB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Photo"
android:checked="true"/>
<ImageView
android:id="@+id/previewIV"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center"
android:visibility="gone"
android:scaleType="centerCrop"
android:layout_marginBottom="16dp"
android:contentDescription="Preview"/>
<RadioButton
android:id="@+id/videoRB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Video"
android:layout_marginStart="24dp"/>
</RadioGroup>
<Button
android:id="@+id/submitBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Post"/>
<TextView
android:id="@+id/categoryLabelTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Category"
android:textSize="16sp"/>
<Button
android:id="@+id/cancelBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Cancel"/>
<Spinner
android:id="@+id/categorySpinner"
android:layout_width="370dp"
android:layout_height="48dp"
android:layout_marginBottom="16dp" />
</LinearLayout>
<TextView
android:id="@+id/selectMediaLabelTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Select Media"
android:textSize="16sp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<Button
android:id="@+id/selectMediaBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Choose File"
android:layout_marginBottom="16dp"/>
<TextView
android:id="@+id/selectedFileTV"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="No file selected"
android:textSize="14sp"
android:layout_marginBottom="16dp"/>
<ImageView
android:id="@+id/previewIV"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center"
android:visibility="gone"
android:scaleType="centerCrop"
android:layout_marginBottom="16dp"
android:contentDescription="Preview"/>
<Button
android:id="@+id/submitBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Post"
android:layout_marginBottom="8dp"/>
<Button
android:id="@+id/cancelBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Cancel"/>
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -85,7 +85,7 @@
android:layout_marginBottom="16dp"
android:contentDescription="Post content"/>
<!-- Post Title -->
<!-- Post Title
<TextView
android:id="@+id/postTitle"
android:layout_width="match_parent"
@ -95,7 +95,7 @@
android:textStyle="bold"
android:textColor="#011f4b"
android:layout_marginBottom="8dp"/>
-->
<!-- Post Description -->
<TextView
android:id="@+id/postDescription"