Compare commits
No commits in common. "fc2d998e91d466d4018824c1edb89728fca9bd81" and "4746c230e1bd90f7e3a4c412e8627da38596148d" have entirely different histories.
fc2d998e91
...
4746c230e1
BIN
ProbnaSlika.png
BIN
ProbnaSlika.png
Binary file not shown.
|
Before Width: | Height: | Size: 5.7 MiB |
@ -40,8 +40,6 @@ 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)
|
||||
|
||||
@ -16,8 +16,7 @@
|
||||
<activity
|
||||
android:name=".NewPostActivity"
|
||||
android:exported="false"
|
||||
tools:ignore="Instantiatable"
|
||||
android:windowSoftInputMode="adjustResize|stateHidden"/>
|
||||
tools:ignore="Instantiatable" />
|
||||
<activity
|
||||
android:name=".RegisterActivity"
|
||||
android:exported="false" />
|
||||
|
||||
@ -4,7 +4,6 @@ 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;
|
||||
@ -13,6 +12,7 @@ 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,51 +20,74 @@ import retrofit2.Response;
|
||||
|
||||
public class LoginActivity extends AppCompatActivity {
|
||||
|
||||
private EditText usernameEdit, passwordEdit;
|
||||
private Button loginBtn;
|
||||
private TextView registerTV;
|
||||
EditText usernameEdit, passwordEdit;
|
||||
Button loginBtn;
|
||||
|
||||
@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 -> login());
|
||||
}
|
||||
|
||||
private void login() {
|
||||
loginBtn.setOnClickListener(v -> {
|
||||
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();
|
||||
Toast.makeText(LoginActivity.this, "All fields are required", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// 🔑 STORE RAW TOKEN ONLY
|
||||
// 🔹 Save token & login state in SharedPreferences
|
||||
getSharedPreferences("MyAppPrefs", MODE_PRIVATE)
|
||||
.edit()
|
||||
.putString("authToken", response.body().getToken())
|
||||
.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();
|
||||
}
|
||||
@ -75,5 +98,6 @@ public class LoginActivity extends AppCompatActivity {
|
||||
Toast.makeText(LoginActivity.this, "Server error: " + t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,135 +4,62 @@ 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 {
|
||||
|
||||
private Button loginBtn, registerBtn, logoutBtn, newPostBtn;
|
||||
private RecyclerView recyclerView;
|
||||
private PostsAdapter postsAdapter;
|
||||
Button loginBtn, registerBtn, logoutBtn;
|
||||
|
||||
@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);
|
||||
|
||||
// RecyclerView setup
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
postsAdapter = new PostsAdapter(new ArrayList<>());
|
||||
recyclerView.setAdapter(postsAdapter);
|
||||
|
||||
setupButtons();
|
||||
updateAuthUI();
|
||||
loadPublications();
|
||||
}
|
||||
|
||||
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();
|
||||
// Button listeners
|
||||
loginBtn.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
|
||||
startActivity(intent);
|
||||
});
|
||||
}
|
||||
|
||||
if (newPostBtn != null) {
|
||||
newPostBtn.setOnClickListener(v ->
|
||||
startActivityForResult(
|
||||
new Intent(MainActivity.this, NewPostActivity.class), 100)
|
||||
);
|
||||
}
|
||||
}
|
||||
registerBtn.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(MainActivity.this, RegisterActivity.class);
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
private void updateAuthUI() {
|
||||
// Check login state from SharedPreferences
|
||||
boolean isLoggedIn = getSharedPreferences("MyAppPrefs", MODE_PRIVATE)
|
||||
.getBoolean("isLoggedIn", false);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private void loadPublications() {
|
||||
ApiService apiService = ApiClient.getClient().create(ApiService.class);
|
||||
|
||||
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);
|
||||
if (isLoggedIn) {
|
||||
// User is logged in → hide login/register, show logout
|
||||
loginBtn.setVisibility(View.GONE);
|
||||
registerBtn.setVisibility(View.GONE);
|
||||
logoutBtn.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
Toast.makeText(MainActivity.this,
|
||||
"Failed to load publications: " + response.code(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
// User not logged in → show login/register, hide logout
|
||||
loginBtn.setVisibility(View.VISIBLE);
|
||||
registerBtn.setVisibility(View.VISIBLE);
|
||||
logoutBtn.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<PublicationListResponse> call, Throwable t) {
|
||||
Toast.makeText(MainActivity.this,
|
||||
"Failed to load publications: " + t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
// 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();
|
||||
|
||||
// Refresh activity to reflect state change
|
||||
recreate();
|
||||
});
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
@ -1,175 +1,4 @@
|
||||
package com.example.gallery;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
public class NewPostActivity {
|
||||
}
|
||||
|
||||
@ -1,104 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,12 +5,10 @@ import retrofit2.converter.gson.GsonConverterFactory;
|
||||
|
||||
public class ApiClient {
|
||||
|
||||
// Base URL of your backend
|
||||
public static final String BASE_URL = "https://gallery.steve-dekart.xyz/";
|
||||
private 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()
|
||||
|
||||
@ -3,25 +3,17 @@ 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);
|
||||
|
||||
@ -30,22 +22,4 @@ 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
|
||||
);
|
||||
}
|
||||
@ -1,48 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
package com.example.gallery.models;
|
||||
|
||||
public class PublicationRequest {
|
||||
public String image;
|
||||
public String video;
|
||||
public String content_type;
|
||||
public String description;
|
||||
public int category;
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
</selector>
|
||||
@ -97,14 +97,14 @@
|
||||
<!-- Register Text -->
|
||||
<TextView
|
||||
android:id="@+id/registerTV"
|
||||
android:layout_width="281dp"
|
||||
android:layout_height="51dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center"
|
||||
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:textSize="14sp" />
|
||||
android:gravity="center"
|
||||
android:clickable="true"
|
||||
android:focusable="true"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?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"
|
||||
@ -9,69 +8,116 @@
|
||||
android:background="@color/bgColor"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<!-- Toolbar -->
|
||||
<androidx.drawerlayout.widget.DrawerLayout
|
||||
android:id="@+id/drawer_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_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="0dp"
|
||||
android:layout_width="match_parent"
|
||||
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"/>
|
||||
android:elevation="4dp"
|
||||
app:title="My App"
|
||||
app:titleTextColor="#011f4b">
|
||||
|
||||
<!-- RecyclerView Feed -->
|
||||
<!-- 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="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
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"/>
|
||||
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>
|
||||
|
||||
<!-- Login Button -->
|
||||
<Button
|
||||
android:id="@+id/Loginbtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="103dp"
|
||||
android:layout_height="51dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="Log in"
|
||||
android:layout_margin="16dp"
|
||||
app:layout_constraintTop_toTopOf="@id/toolbar"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<!-- Register Button -->
|
||||
<Button
|
||||
android:id="@+id/Registerbtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Register"
|
||||
android:layout_marginStart="16dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/Loginbtn"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
android:layout_marginTop="76dp"
|
||||
android:text="Register"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<!-- Logout Button -->
|
||||
<Button
|
||||
android:id="@+id/btnLogout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="101dp"
|
||||
android:layout_height="58dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="Logout"
|
||||
android:layout_margin="16dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toTopOf="@id/toolbar"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<!-- 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>
|
||||
@ -6,13 +6,9 @@
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".NewPostActivity">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
@ -24,6 +20,21 @@
|
||||
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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Enter post title"
|
||||
android:inputType="text"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/descriptionLabelTV"
|
||||
android:layout_width="wrap_content"
|
||||
@ -67,21 +78,9 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Video"
|
||||
android:layout_marginStart="24dp"/>
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/categoryLabelTV"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Category"
|
||||
android:textSize="16sp"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/categorySpinner"
|
||||
android:layout_width="370dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginBottom="16dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/selectMediaLabelTV"
|
||||
android:layout_width="wrap_content"
|
||||
@ -118,14 +117,14 @@
|
||||
android:id="@+id/submitBtn"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Post"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
android:text="Post"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/cancelBtn"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Cancel"/>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -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"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user