Refactor usage of dagger application with fixed dagger application

This commit is contained in:
maskara 2018-01-13 01:40:15 +05:30
parent 1224302ccb
commit e1afa6081e
32 changed files with 556 additions and 311 deletions

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons; package fr.free.nrw.commons;
import android.app.Application;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
@ -18,15 +19,12 @@ import java.io.File;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import dagger.android.AndroidInjector;
import dagger.android.DaggerApplication;
import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.category.CategoryDao; import fr.free.nrw.commons.category.CategoryDao;
import fr.free.nrw.commons.contributions.ContributionDao; import fr.free.nrw.commons.contributions.ContributionDao;
import fr.free.nrw.commons.data.DBOpenHelper; import fr.free.nrw.commons.data.DBOpenHelper;
import fr.free.nrw.commons.di.ApplicationlessInjection;
import fr.free.nrw.commons.di.CommonsApplicationComponent; import fr.free.nrw.commons.di.CommonsApplicationComponent;
import fr.free.nrw.commons.di.CommonsApplicationModule;
import fr.free.nrw.commons.di.DaggerCommonsApplicationComponent;
import fr.free.nrw.commons.modifications.ModifierSequenceDao; import fr.free.nrw.commons.modifications.ModifierSequenceDao;
import fr.free.nrw.commons.utils.FileUtils; import fr.free.nrw.commons.utils.FileUtils;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
@ -42,10 +40,11 @@ import timber.log.Timber;
resDialogCommentPrompt = R.string.crash_dialog_comment_prompt, resDialogCommentPrompt = R.string.crash_dialog_comment_prompt,
resDialogOkToast = R.string.crash_dialog_ok_toast resDialogOkToast = R.string.crash_dialog_ok_toast
) )
public class CommonsApplication extends DaggerApplication { public class CommonsApplication extends Application {
@Inject SessionManager sessionManager; @Inject SessionManager sessionManager;
@Inject DBOpenHelper dbOpenHelper; @Inject DBOpenHelper dbOpenHelper;
@Inject @Named("default_preferences") SharedPreferences defaultPrefs; @Inject @Named("default_preferences") SharedPreferences defaultPrefs;
@Inject @Named("application_preferences") SharedPreferences applicationPrefs; @Inject @Named("application_preferences") SharedPreferences applicationPrefs;
@Inject @Named("prefs") SharedPreferences otherPrefs; @Inject @Named("prefs") SharedPreferences otherPrefs;
@ -61,6 +60,7 @@ public class CommonsApplication extends DaggerApplication {
private CommonsApplicationComponent component; private CommonsApplicationComponent component;
private RefWatcher refWatcher; private RefWatcher refWatcher;
/** /**
* Used to declare and initialize various components and dependencies * Used to declare and initialize various components and dependencies
*/ */
@ -68,6 +68,11 @@ public class CommonsApplication extends DaggerApplication {
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
ApplicationlessInjection
.getInstance(this)
.getCommonsApplicationComponent()
.inject(this);
Fresco.initialize(this); Fresco.initialize(this);
if (setupLeakCanary() == RefWatcher.DISABLED) { if (setupLeakCanary() == RefWatcher.DISABLED) {
return; return;
@ -85,6 +90,7 @@ public class CommonsApplication extends DaggerApplication {
System.setProperty("in.yuvi.http.fluent.PROGRESS_TRIGGER_THRESHOLD", "3.0"); System.setProperty("in.yuvi.http.fluent.PROGRESS_TRIGGER_THRESHOLD", "3.0");
} }
/** /**
* Helps in setting up LeakCanary library * Helps in setting up LeakCanary library
* @return instance of LeakCanary * @return instance of LeakCanary
@ -107,28 +113,6 @@ public class CommonsApplication extends DaggerApplication {
return application.refWatcher; return application.refWatcher;
} }
/**
* Helps in injecting dependency library Dagger
* @return Dagger injector
*/
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return injector();
}
/**
* used to create injector of application component
* @return Application component of Dagger
*/
public CommonsApplicationComponent injector() {
if (component == null) {
component = DaggerCommonsApplicationComponent.builder()
.appModule(new CommonsApplicationModule(this))
.build();
}
return component;
}
/** /**
* clears data of current application * clears data of current application
* @param context Application context * @param context Application context

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons; package fr.free.nrw.commons;
import android.app.Service;
import android.content.Intent; import android.content.Intent;
import android.os.Binder; import android.os.Binder;
import android.os.Handler; import android.os.Handler;
@ -8,9 +9,11 @@ import android.os.IBinder;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import dagger.android.AndroidInjection;
import dagger.android.DaggerService; import dagger.android.DaggerService;
import fr.free.nrw.commons.di.FixedDaggerService;
public abstract class HandlerService<T> extends DaggerService { public abstract class HandlerService<T> extends FixedDaggerService {
private volatile Looper threadLooper; private volatile Looper threadLooper;
private volatile ServiceHandler threadHandler; private volatile ServiceHandler threadHandler;
private String serviceName; private String serviceName;

View file

@ -14,6 +14,7 @@ import com.facebook.drawee.view.SimpleDraweeView;
import javax.inject.Inject; import javax.inject.Inject;
import fr.free.nrw.commons.di.ApplicationlessInjection;
import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.mwapi.MediaWikiApi;
import timber.log.Timber; import timber.log.Timber;
@ -71,7 +72,11 @@ public class MediaWikiImageView extends SimpleDraweeView {
* Initializes MediaWikiImageView. * Initializes MediaWikiImageView.
*/ */
private void init() { private void init() {
((CommonsApplication) getContext().getApplicationContext()).injector().inject(this); ApplicationlessInjection
.getInstance(getContext()
.getApplicationContext())
.getCommonsApplicationComponent()
.inject(this);
setHierarchy(GenericDraweeHierarchyBuilder setHierarchy(GenericDraweeHierarchyBuilder
.newInstance(getResources()) .newInstance(getResources())
.setPlaceholderImage(VectorDrawableCompat.create(getResources(), .setPlaceholderImage(VectorDrawableCompat.create(getResources(),

View file

@ -33,6 +33,7 @@ import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.WelcomeActivity; import fr.free.nrw.commons.WelcomeActivity;
import fr.free.nrw.commons.contributions.ContributionsActivity; import fr.free.nrw.commons.contributions.ContributionsActivity;
import fr.free.nrw.commons.di.ApplicationlessInjection;
import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.mwapi.MediaWikiApi;
import fr.free.nrw.commons.theme.NavigationBaseActivity; import fr.free.nrw.commons.theme.NavigationBaseActivity;
import timber.log.Timber; import timber.log.Timber;
@ -66,8 +67,12 @@ public class LoginActivity extends AccountAuthenticatorActivity {
setTheme(Utils.isDarkTheme(this) ? R.style.DarkAppTheme : R.style.LightAppTheme); setTheme(Utils.isDarkTheme(this) ? R.style.DarkAppTheme : R.style.LightAppTheme);
getDelegate().installViewFactory(); getDelegate().installViewFactory();
getDelegate().onCreate(savedInstanceState); getDelegate().onCreate(savedInstanceState);
AndroidInjection.inject(this);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ApplicationlessInjection
.getInstance(this.getApplicationContext())
.getCommonsApplicationComponent()
.inject(this);
setContentView(R.layout.activity_login); setContentView(R.layout.activity_login);

View file

@ -1,20 +1,28 @@
package fr.free.nrw.commons.auth; package fr.free.nrw.commons.auth;
import android.app.Service;
import android.content.Intent; import android.content.Intent;
import android.os.IBinder; import android.os.IBinder;
import javax.inject.Inject; import javax.inject.Inject;
import dagger.android.AndroidInjection;
import dagger.android.DaggerService; import dagger.android.DaggerService;
import fr.free.nrw.commons.di.FixedDaggerService;
import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.mwapi.MediaWikiApi;
import static android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT; import static android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT;
public class WikiAccountAuthenticatorService extends DaggerService { public class WikiAccountAuthenticatorService extends FixedDaggerService {
@Inject MediaWikiApi mwApi; @Inject MediaWikiApi mwApi;
private WikiAccountAuthenticator wikiAccountAuthenticator = null; private WikiAccountAuthenticator wikiAccountAuthenticator = null;
@Override
public void onCreate() {
super.onCreate();
}
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
if (!intent.getAction().equals(ACTION_AUTHENTICATOR_INTENT)) { if (!intent.getAction().equals(ACTION_AUTHENTICATOR_INTENT)) {

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons.category; package fr.free.nrw.commons.category;
import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
@ -33,8 +34,8 @@ import javax.inject.Named;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import dagger.android.support.DaggerFragment;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.di.FixedDaggerFragment;
import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.mwapi.MediaWikiApi;
import fr.free.nrw.commons.upload.MwVolleyApi; import fr.free.nrw.commons.upload.MwVolleyApi;
import fr.free.nrw.commons.utils.StringSortingUtils; import fr.free.nrw.commons.utils.StringSortingUtils;
@ -49,7 +50,7 @@ import static android.view.KeyEvent.KEYCODE_BACK;
/** /**
* Displays the category suggestion and selection screen. Category search is initiated here. * Displays the category suggestion and selection screen. Category search is initiated here.
*/ */
public class CategorizationFragment extends DaggerFragment { public class CategorizationFragment extends FixedDaggerFragment {
public static final int SEARCH_CATS_LIMIT = 25; public static final int SEARCH_CATS_LIMIT = 25;
@ -82,6 +83,11 @@ public class CategorizationFragment extends DaggerFragment {
} }
}); });
@Override
public void onAttach(Context context) {
super.onAttach(context);
}
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {

View file

@ -12,8 +12,10 @@ import android.text.TextUtils;
import javax.inject.Inject; import javax.inject.Inject;
import dagger.Lazy;
import dagger.android.AndroidInjection; import dagger.android.AndroidInjection;
import fr.free.nrw.commons.data.DBOpenHelper; import fr.free.nrw.commons.data.DBOpenHelper;
import fr.free.nrw.commons.di.FixedDaggerContentProvider;
import timber.log.Timber; import timber.log.Timber;
import static android.content.UriMatcher.NO_MATCH; import static android.content.UriMatcher.NO_MATCH;
@ -21,7 +23,7 @@ import static fr.free.nrw.commons.category.CategoryDao.Table.ALL_FIELDS;
import static fr.free.nrw.commons.category.CategoryDao.Table.COLUMN_ID; import static fr.free.nrw.commons.category.CategoryDao.Table.COLUMN_ID;
import static fr.free.nrw.commons.category.CategoryDao.Table.TABLE_NAME; import static fr.free.nrw.commons.category.CategoryDao.Table.TABLE_NAME;
public class CategoryContentProvider extends ContentProvider { public class CategoryContentProvider extends FixedDaggerContentProvider {
public static final String AUTHORITY = "fr.free.nrw.commons.categories.contentprovider"; public static final String AUTHORITY = "fr.free.nrw.commons.categories.contentprovider";
// For URI matcher // For URI matcher
@ -42,12 +44,12 @@ public class CategoryContentProvider extends ContentProvider {
return Uri.parse(BASE_URI.toString() + "/" + id); return Uri.parse(BASE_URI.toString() + "/" + id);
} }
@Inject DBOpenHelper dbOpenHelper; @Inject Lazy<DBOpenHelper> dbOpenHelper;
@Override @Override
public boolean onCreate() { public boolean onCreate() {
AndroidInjection.inject(this); super.onCreate();
return false; return true;
} }
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
@ -59,7 +61,7 @@ public class CategoryContentProvider extends ContentProvider {
int uriType = uriMatcher.match(uri); int uriType = uriMatcher.match(uri);
SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); SQLiteDatabase db = dbOpenHelper.get().getReadableDatabase();
Cursor cursor; Cursor cursor;
switch (uriType) { switch (uriType) {
@ -95,7 +97,7 @@ public class CategoryContentProvider extends ContentProvider {
@Override @Override
public Uri insert(@NonNull Uri uri, ContentValues contentValues) { public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
int uriType = uriMatcher.match(uri); int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); SQLiteDatabase sqlDB = dbOpenHelper.get().getWritableDatabase();
long id; long id;
switch (uriType) { switch (uriType) {
case CATEGORIES: case CATEGORIES:
@ -118,7 +120,7 @@ public class CategoryContentProvider extends ContentProvider {
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) { public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
Timber.d("Hello, bulk insert! (CategoryContentProvider)"); Timber.d("Hello, bulk insert! (CategoryContentProvider)");
int uriType = uriMatcher.match(uri); int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); SQLiteDatabase sqlDB = dbOpenHelper.get().getWritableDatabase();
sqlDB.beginTransaction(); sqlDB.beginTransaction();
switch (uriType) { switch (uriType) {
case CATEGORIES: case CATEGORIES:
@ -150,7 +152,7 @@ public class CategoryContentProvider extends ContentProvider {
and will error out otherwise. and will error out otherwise.
*/ */
int uriType = uriMatcher.match(uri); int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); SQLiteDatabase sqlDB = dbOpenHelper.get().getWritableDatabase();
int rowsUpdated; int rowsUpdated;
switch (uriType) { switch (uriType) {
case CATEGORIES_ID: case CATEGORIES_ID:

View file

@ -12,15 +12,17 @@ import android.text.TextUtils;
import javax.inject.Inject; import javax.inject.Inject;
import dagger.Lazy;
import dagger.android.AndroidInjection; import dagger.android.AndroidInjection;
import fr.free.nrw.commons.data.DBOpenHelper; import fr.free.nrw.commons.data.DBOpenHelper;
import fr.free.nrw.commons.di.FixedDaggerContentProvider;
import timber.log.Timber; import timber.log.Timber;
import static android.content.UriMatcher.NO_MATCH; import static android.content.UriMatcher.NO_MATCH;
import static fr.free.nrw.commons.contributions.ContributionDao.Table.ALL_FIELDS; import static fr.free.nrw.commons.contributions.ContributionDao.Table.ALL_FIELDS;
import static fr.free.nrw.commons.contributions.ContributionDao.Table.TABLE_NAME; import static fr.free.nrw.commons.contributions.ContributionDao.Table.TABLE_NAME;
public class ContributionsContentProvider extends ContentProvider { public class ContributionsContentProvider extends FixedDaggerContentProvider {
private static final int CONTRIBUTIONS = 1; private static final int CONTRIBUTIONS = 1;
private static final int CONTRIBUTIONS_ID = 2; private static final int CONTRIBUTIONS_ID = 2;
@ -39,11 +41,12 @@ public class ContributionsContentProvider extends ContentProvider {
return Uri.parse(BASE_URI.toString() + "/" + id); return Uri.parse(BASE_URI.toString() + "/" + id);
} }
@Inject DBOpenHelper dbOpenHelper; @Inject
Lazy<DBOpenHelper> dbOpenHelper;
@Override @Override
public boolean onCreate() { public boolean onCreate() {
AndroidInjection.inject(this); super.onCreate();
return true; return true;
} }
@ -56,7 +59,7 @@ public class ContributionsContentProvider extends ContentProvider {
int uriType = uriMatcher.match(uri); int uriType = uriMatcher.match(uri);
SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); SQLiteDatabase db = dbOpenHelper.get().getReadableDatabase();
Cursor cursor; Cursor cursor;
switch (uriType) { switch (uriType) {
@ -92,7 +95,7 @@ public class ContributionsContentProvider extends ContentProvider {
@Override @Override
public Uri insert(@NonNull Uri uri, ContentValues contentValues) { public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
int uriType = uriMatcher.match(uri); int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); SQLiteDatabase sqlDB = dbOpenHelper.get().getWritableDatabase();
long id = 0; long id = 0;
switch (uriType) { switch (uriType) {
case CONTRIBUTIONS: case CONTRIBUTIONS:
@ -111,7 +114,7 @@ public class ContributionsContentProvider extends ContentProvider {
int rows; int rows;
int uriType = uriMatcher.match(uri); int uriType = uriMatcher.match(uri);
SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); SQLiteDatabase db = dbOpenHelper.get().getReadableDatabase();
switch (uriType) { switch (uriType) {
case CONTRIBUTIONS_ID: case CONTRIBUTIONS_ID:
@ -133,7 +136,7 @@ public class ContributionsContentProvider extends ContentProvider {
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) { public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
Timber.d("Hello, bulk insert! (ContributionsContentProvider)"); Timber.d("Hello, bulk insert! (ContributionsContentProvider)");
int uriType = uriMatcher.match(uri); int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); SQLiteDatabase sqlDB = dbOpenHelper.get().getWritableDatabase();
sqlDB.beginTransaction(); sqlDB.beginTransaction();
switch (uriType) { switch (uriType) {
case CONTRIBUTIONS: case CONTRIBUTIONS:
@ -164,7 +167,7 @@ public class ContributionsContentProvider extends ContentProvider {
error out otherwise. error out otherwise.
*/ */
int uriType = uriMatcher.match(uri); int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); SQLiteDatabase sqlDB = dbOpenHelper.get().getWritableDatabase();
int rowsUpdated = 0; int rowsUpdated = 0;
switch (uriType) { switch (uriType) {
case CONTRIBUTIONS: case CONTRIBUTIONS:

View file

@ -1,11 +1,13 @@
package fr.free.nrw.commons.contributions; package fr.free.nrw.commons.contributions;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -25,8 +27,10 @@ import javax.inject.Named;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import dagger.android.support.AndroidSupportInjection;
import dagger.android.support.DaggerFragment; import dagger.android.support.DaggerFragment;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.di.FixedDaggerFragment;
import fr.free.nrw.commons.nearby.NearbyActivity; import fr.free.nrw.commons.nearby.NearbyActivity;
import timber.log.Timber; import timber.log.Timber;
@ -36,7 +40,7 @@ import static android.app.Activity.RESULT_OK;
import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.view.View.GONE; import static android.view.View.GONE;
public class ContributionsListFragment extends DaggerFragment { public class ContributionsListFragment extends FixedDaggerFragment {
@BindView(R.id.contributionsList) @BindView(R.id.contributionsList)
GridView contributionsList; GridView contributionsList;
@ -75,6 +79,11 @@ public class ContributionsListFragment extends DaggerFragment {
return v; return v;
} }
@Override
public void onAttach(Context context) {
super.onAttach(context);
}
public ListAdapter getAdapter() { public ListAdapter getAdapter() {
return contributionsList.getAdapter(); return contributionsList.getAdapter();
} }

View file

@ -25,6 +25,7 @@ import javax.inject.Named;
import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.di.ApplicationlessInjection;
import fr.free.nrw.commons.mwapi.LogEventResult; import fr.free.nrw.commons.mwapi.LogEventResult;
import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.mwapi.MediaWikiApi;
import timber.log.Timber; import timber.log.Timber;
@ -81,7 +82,11 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
@Override @Override
public void onPerformSync(Account account, Bundle bundle, String authority, public void onPerformSync(Account account, Bundle bundle, String authority,
ContentProviderClient contentProviderClient, SyncResult syncResult) { ContentProviderClient contentProviderClient, SyncResult syncResult) {
((CommonsApplication) getContext().getApplicationContext()).injector().inject(this); ApplicationlessInjection
.getInstance(getContext()
.getApplicationContext())
.getCommonsApplicationComponent()
.inject(this);
// This code is fraught with possibilities of race conditions, but lalalalala I can't hear you! // This code is fraught with possibilities of race conditions, but lalalalala I can't hear you!
String user = account.name; String user = account.name;
String lastModified = prefs.getString("lastSyncTimestamp", ""); String lastModified = prefs.getString("lastSyncTimestamp", "");

View file

@ -0,0 +1,10 @@
package fr.free.nrw.commons.di;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Qualifier;
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationContext {}

View file

@ -0,0 +1,99 @@
package fr.free.nrw.commons.di;
import android.app.Activity;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ContentProvider;
import android.content.Context;
import android.support.v4.app.Fragment;
import javax.inject.Inject;
import dagger.android.AndroidInjector;
import dagger.android.DispatchingAndroidInjector;
import dagger.android.HasActivityInjector;
import dagger.android.HasBroadcastReceiverInjector;
import dagger.android.HasContentProviderInjector;
import dagger.android.HasFragmentInjector;
import dagger.android.HasServiceInjector;
import dagger.android.support.HasSupportFragmentInjector;
public class ApplicationlessInjection
implements
HasActivityInjector,
HasFragmentInjector,
HasSupportFragmentInjector,
HasServiceInjector,
HasBroadcastReceiverInjector,
HasContentProviderInjector {
private static ApplicationlessInjection instance = null;
@Inject
DispatchingAndroidInjector<Activity> activityInjector;
@Inject
DispatchingAndroidInjector<BroadcastReceiver> broadcastReceiverInjector;
@Inject
DispatchingAndroidInjector<android.app.Fragment> fragmentInjector;
@Inject
DispatchingAndroidInjector<Fragment> supportFragmentInjector;
@Inject
DispatchingAndroidInjector<Service> serviceInjector;
@Inject
DispatchingAndroidInjector<ContentProvider> contentProviderInjector;
private CommonsApplicationComponent commonsApplicationComponent;
public ApplicationlessInjection(Context applicationContext) {
commonsApplicationComponent = DaggerCommonsApplicationComponent.builder()
.appModule(new CommonsApplicationModule(applicationContext)).build();
commonsApplicationComponent.inject(this);
}
@Override
public DispatchingAndroidInjector<Activity> activityInjector() {
return activityInjector;
}
@Override
public DispatchingAndroidInjector<android.app.Fragment> fragmentInjector() {
return fragmentInjector;
}
@Override
public DispatchingAndroidInjector<Fragment> supportFragmentInjector() {
return supportFragmentInjector;
}
@Override
public DispatchingAndroidInjector<BroadcastReceiver> broadcastReceiverInjector() {
return broadcastReceiverInjector;
}
@Override
public DispatchingAndroidInjector<Service> serviceInjector() {
return serviceInjector;
}
@Override
public AndroidInjector<ContentProvider> contentProviderInjector() {
return contentProviderInjector;
}
public CommonsApplicationComponent getCommonsApplicationComponent() {
return commonsApplicationComponent;
}
public static ApplicationlessInjection getInstance(Context applicationContext) {
if (instance == null) {
synchronized (ApplicationlessInjection.class) {
if (instance == null) {
instance = new ApplicationlessInjection(applicationContext);
}
}
}
return instance;
}
}

View file

@ -1,5 +1,7 @@
package fr.free.nrw.commons.di; package fr.free.nrw.commons.di;
import android.content.Context;
import javax.inject.Singleton; import javax.inject.Singleton;
import dagger.Component; import dagger.Component;
@ -8,8 +10,13 @@ import dagger.android.AndroidInjector;
import dagger.android.support.AndroidSupportInjectionModule; import dagger.android.support.AndroidSupportInjectionModule;
import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.MediaWikiImageView; import fr.free.nrw.commons.MediaWikiImageView;
import fr.free.nrw.commons.auth.LoginActivity;
import fr.free.nrw.commons.category.CategoryContentProvider;
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
import fr.free.nrw.commons.contributions.ContributionsSyncAdapter; import fr.free.nrw.commons.contributions.ContributionsSyncAdapter;
import fr.free.nrw.commons.modifications.ModificationsContentProvider;
import fr.free.nrw.commons.modifications.ModificationsSyncAdapter; import fr.free.nrw.commons.modifications.ModificationsSyncAdapter;
import fr.free.nrw.commons.settings.SettingsFragment;
@Singleton @Singleton
@Component(modules = { @Component(modules = {
@ -21,7 +28,7 @@ import fr.free.nrw.commons.modifications.ModificationsSyncAdapter;
ServiceBuilderModule.class, ServiceBuilderModule.class,
ContentProviderBuilderModule.class ContentProviderBuilderModule.class
}) })
public interface CommonsApplicationComponent extends AndroidInjector<CommonsApplication> { public interface CommonsApplicationComponent extends AndroidInjector<ApplicationlessInjection> {
void inject(CommonsApplication application); void inject(CommonsApplication application);
void inject(ContributionsSyncAdapter syncAdapter); void inject(ContributionsSyncAdapter syncAdapter);
@ -30,6 +37,13 @@ public interface CommonsApplicationComponent extends AndroidInjector<CommonsAppl
void inject(MediaWikiImageView mediaWikiImageView); void inject(MediaWikiImageView mediaWikiImageView);
void inject(LoginActivity activity);
void inject(SettingsFragment fragment);
@Override
void inject(ApplicationlessInjection instance);
@Component.Builder @Component.Builder
@SuppressWarnings({"WeakerAccess", "unused"}) @SuppressWarnings({"WeakerAccess", "unused"})
interface Builder { interface Builder {

View file

@ -1,6 +1,7 @@
package fr.free.nrw.commons.di; package fr.free.nrw.commons.di;
import android.content.ContentProviderClient; import android.content.ContentProviderClient;
import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.v4.util.LruCache; import android.support.v4.util.LruCache;
@ -8,10 +9,10 @@ import android.support.v4.util.LruCache;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import dagger.Binds;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import fr.free.nrw.commons.BuildConfig; import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.auth.AccountUtil; import fr.free.nrw.commons.auth.AccountUtil;
import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.caching.CacheController; import fr.free.nrw.commons.caching.CacheController;
@ -31,62 +32,70 @@ import static fr.free.nrw.commons.modifications.ModificationsContentProvider.MOD
public class CommonsApplicationModule { public class CommonsApplicationModule {
public static final String CATEGORY_AUTHORITY = "fr.free.nrw.commons.categories.contentprovider"; public static final String CATEGORY_AUTHORITY = "fr.free.nrw.commons.categories.contentprovider";
private CommonsApplication application; private Context applicationContext;
public CommonsApplicationModule(CommonsApplication application) { public CommonsApplicationModule(Context applicationContext) {
this.application = application; this.applicationContext = applicationContext;
} }
@Provides @Provides
public AccountUtil providesAccountUtil() { public Context providesApplicationContext() {
return new AccountUtil(application); return this.applicationContext;
}
@Provides
public AccountUtil providesAccountUtil(Context context) {
return new AccountUtil(context);
} }
@Provides @Provides
@Named("category") @Named("category")
public ContentProviderClient provideCategoryContentProviderClient() { public ContentProviderClient provideCategoryContentProviderClient(Context context) {
return application.getContentResolver().acquireContentProviderClient(CATEGORY_AUTHORITY); return context.getContentResolver().acquireContentProviderClient(CATEGORY_AUTHORITY);
} }
@Provides @Provides
@Named("contribution") @Named("contribution")
public ContentProviderClient provideContributionContentProviderClient() { public ContentProviderClient provideContributionContentProviderClient(Context context) {
return application.getContentResolver().acquireContentProviderClient(CONTRIBUTION_AUTHORITY); return context.getContentResolver().acquireContentProviderClient(CONTRIBUTION_AUTHORITY);
} }
@Provides @Provides
@Named("modification") @Named("modification")
public ContentProviderClient provideModificationContentProviderClient() { public ContentProviderClient provideModificationContentProviderClient(Context context) {
return application.getContentResolver().acquireContentProviderClient(MODIFICATIONS_AUTHORITY); return context.getContentResolver().acquireContentProviderClient(MODIFICATIONS_AUTHORITY);
} }
@Provides @Provides
@Named("application_preferences") @Named("application_preferences")
public SharedPreferences providesApplicationSharedPreferences() { public SharedPreferences providesApplicationSharedPreferences(Context context) {
return application.getSharedPreferences("fr.free.nrw.commons", MODE_PRIVATE); return context.getSharedPreferences("fr.free.nrw.commons", MODE_PRIVATE);
} }
@Provides @Provides
@Named("default_preferences") @Named("default_preferences")
public SharedPreferences providesDefaultSharedPreferences() { public SharedPreferences providesDefaultSharedPreferences(Context context) {
return PreferenceManager.getDefaultSharedPreferences(application); return PreferenceManager.getDefaultSharedPreferences(context);
} }
@Provides @Provides
@Named("prefs") @Named("prefs")
public SharedPreferences providesOtherSharedPreferences() { public SharedPreferences providesOtherSharedPreferences(Context context) {
return application.getSharedPreferences("prefs", MODE_PRIVATE); return context.getSharedPreferences("prefs", MODE_PRIVATE);
} }
@Provides @Provides
public UploadController providesUploadController(SessionManager sessionManager, @Named("default_preferences") SharedPreferences sharedPreferences) { public UploadController providesUploadController(Context context,
return new UploadController(sessionManager, application, sharedPreferences); SessionManager sessionManager,
@Named("default_preferences") SharedPreferences sharedPreferences) {
return new UploadController(sessionManager, context, sharedPreferences);
} }
@Provides @Provides
@Singleton @Singleton
public SessionManager providesSessionManager(MediaWikiApi mediaWikiApi) { public SessionManager providesSessionManager(Context context,
return new SessionManager(application, mediaWikiApi); MediaWikiApi mediaWikiApi) {
return new SessionManager(context, mediaWikiApi);
} }
@Provides @Provides
@ -97,8 +106,8 @@ public class CommonsApplicationModule {
@Provides @Provides
@Singleton @Singleton
public LocationServiceManager provideLocationServiceManager() { public LocationServiceManager provideLocationServiceManager(Context context) {
return new LocationServiceManager(application); return new LocationServiceManager(context);
} }
@Provides @Provides
@ -109,8 +118,8 @@ public class CommonsApplicationModule {
@Provides @Provides
@Singleton @Singleton
public DBOpenHelper provideDBOpenHelper() { public DBOpenHelper provideDBOpenHelper(Context context) {
return new DBOpenHelper(application); return new DBOpenHelper(context);
} }
@Provides @Provides

View file

@ -0,0 +1,43 @@
package fr.free.nrw.commons.di;
import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import javax.inject.Inject;
import dagger.android.AndroidInjector;
import dagger.android.DispatchingAndroidInjector;
import dagger.android.support.HasSupportFragmentInjector;
public abstract class FixedDaggerAppCompatActivity extends AppCompatActivity implements HasSupportFragmentInjector {
@Inject
DispatchingAndroidInjector<Fragment> supportFragmentInjector;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
inject();
super.onCreate(savedInstanceState);
}
@Override
public AndroidInjector<Fragment> supportFragmentInjector() {
return supportFragmentInjector;
}
private void inject() {
ApplicationlessInjection injection = ApplicationlessInjection.getInstance(getApplicationContext());
AndroidInjector<Activity> activityInjector = injection.activityInjector();
if (activityInjector == null) {
throw new NullPointerException("ApplicationlessInjection.activityInjector() returned null");
}
activityInjector.inject(this);
}
}

View file

@ -0,0 +1,31 @@
package fr.free.nrw.commons.di;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import dagger.android.AndroidInjector;
public abstract class FixedDaggerBroadcastReceiver extends BroadcastReceiver {
public FixedDaggerBroadcastReceiver() {
super();
}
@Override
public void onReceive(Context context, Intent intent) {
inject(context);
}
private void inject(Context context) {
ApplicationlessInjection injection = ApplicationlessInjection.getInstance(context.getApplicationContext());
AndroidInjector<BroadcastReceiver> serviceInjector = injection.broadcastReceiverInjector();
if (serviceInjector == null) {
throw new NullPointerException("ApplicationlessInjection.broadcastReceiverInjector() returned null");
}
serviceInjector.inject(this);
}
}

View file

@ -0,0 +1,32 @@
package fr.free.nrw.commons.di;
import android.content.ContentProvider;
import dagger.android.AndroidInjector;
public abstract class FixedDaggerContentProvider extends ContentProvider {
public FixedDaggerContentProvider() {
super();
}
@Override
public boolean onCreate() {
inject();
return true;
}
private void inject() {
ApplicationlessInjection injection = ApplicationlessInjection.getInstance(getContext());
AndroidInjector<ContentProvider> serviceInjector = injection.contentProviderInjector();
if (serviceInjector == null) {
throw new NullPointerException("ApplicationlessInjection.contentProviderInjector() returned null");
}
serviceInjector.inject(this);
}
}

View file

@ -0,0 +1,65 @@
package fr.free.nrw.commons.di;
import android.app.Activity;
import android.content.Context;
import android.support.v4.app.Fragment;
import javax.inject.Inject;
import dagger.android.AndroidInjector;
import dagger.android.DispatchingAndroidInjector;
import dagger.android.support.HasSupportFragmentInjector;
public abstract class FixedDaggerFragment extends Fragment implements HasSupportFragmentInjector {
@Inject
DispatchingAndroidInjector<Fragment> childFragmentInjector;
@Override
public void onAttach(Context context) {
inject();
super.onAttach(context);
}
@Override
public AndroidInjector<Fragment> supportFragmentInjector() {
return childFragmentInjector;
}
public void inject() {
HasSupportFragmentInjector hasSupportFragmentInjector = findHasFragmentInjector();
AndroidInjector<Fragment> fragmentInjector = hasSupportFragmentInjector.supportFragmentInjector();
if (fragmentInjector == null) {
throw new NullPointerException(String.format("%s.supportFragmentInjector() returned null", hasSupportFragmentInjector.getClass().getCanonicalName()));
}
fragmentInjector.inject(this);
}
private HasSupportFragmentInjector findHasFragmentInjector() {
Fragment parentFragment = this;
while ((parentFragment = parentFragment.getParentFragment()) != null) {
if (parentFragment instanceof HasSupportFragmentInjector) {
return (HasSupportFragmentInjector) parentFragment;
}
}
Activity activity = getActivity();
if (activity instanceof HasSupportFragmentInjector) {
return (HasSupportFragmentInjector) activity;
}
ApplicationlessInjection injection = ApplicationlessInjection.getInstance(activity.getApplicationContext());
if (injection != null) {
return injection;
}
throw new IllegalArgumentException(String.format("No injector was found for %s", getClass().getCanonicalName()));
}
}

View file

@ -0,0 +1,32 @@
package fr.free.nrw.commons.di;
import android.app.IntentService;
import android.app.Service;
import dagger.android.AndroidInjector;
public abstract class FixedDaggerIntentService extends IntentService {
public FixedDaggerIntentService(String name) {
super(name);
}
@Override
public void onCreate() {
inject();
super.onCreate();
}
private void inject() {
ApplicationlessInjection injection = ApplicationlessInjection.getInstance(getApplicationContext());
AndroidInjector<Service> serviceInjector = injection.serviceInjector();
if (serviceInjector == null) {
throw new NullPointerException("ApplicationlessInjection.serviceInjector() returned null");
}
serviceInjector.inject(this);
}
}

View file

@ -0,0 +1,31 @@
package fr.free.nrw.commons.di;
import android.app.Service;
import dagger.android.AndroidInjector;
public abstract class FixedDaggerService extends Service {
public FixedDaggerService() {
super();
}
@Override
public void onCreate() {
inject();
super.onCreate();
}
private void inject() {
ApplicationlessInjection injection = ApplicationlessInjection.getInstance(getApplicationContext());
AndroidInjector<Service> serviceInjector = injection.serviceInjector();
if (serviceInjector == null) {
throw new NullPointerException("ApplicationlessInjection.serviceInjector() returned null");
}
serviceInjector.inject(this);
}
}

View file

@ -1,11 +1,13 @@
package fr.free.nrw.commons.media; package fr.free.nrw.commons.media;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.database.DataSetObserver; import android.database.DataSetObserver;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -24,6 +26,7 @@ import java.util.Locale;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Provider; import javax.inject.Provider;
import dagger.android.support.AndroidSupportInjection;
import dagger.android.support.DaggerFragment; import dagger.android.support.DaggerFragment;
import fr.free.nrw.commons.License; import fr.free.nrw.commons.License;
import fr.free.nrw.commons.LicenseList; import fr.free.nrw.commons.LicenseList;
@ -37,7 +40,7 @@ import fr.free.nrw.commons.mwapi.MediaWikiApi;
import fr.free.nrw.commons.ui.widget.CompatTextView; import fr.free.nrw.commons.ui.widget.CompatTextView;
import timber.log.Timber; import timber.log.Timber;
public class MediaDetailFragment extends DaggerFragment { public class MediaDetailFragment extends Fragment {
private boolean editable; private boolean editable;
private MediaDetailPagerFragment.MediaDetailProvider detailProvider; private MediaDetailPagerFragment.MediaDetailProvider detailProvider;
@ -57,6 +60,12 @@ public class MediaDetailFragment extends DaggerFragment {
return mf; return mf;
} }
@Override
public void onAttach(Context context) {
AndroidSupportInjection.inject(this);
super.onAttach(context);
}
@Inject @Inject
Provider<MediaDataExtractor> mediaDataExtractorProvider; Provider<MediaDataExtractor> mediaDataExtractorProvider;

View file

@ -2,6 +2,7 @@ package fr.free.nrw.commons.media;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.DownloadManager; import android.app.DownloadManager;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.database.DataSetObserver; import android.database.DataSetObserver;
@ -28,6 +29,7 @@ import android.view.ViewGroup;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import dagger.android.support.AndroidSupportInjection;
import dagger.android.support.DaggerFragment; import dagger.android.support.DaggerFragment;
import fr.free.nrw.commons.Media; import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
@ -41,7 +43,7 @@ import static android.content.Context.DOWNLOAD_SERVICE;
import static android.content.Intent.ACTION_VIEW; import static android.content.Intent.ACTION_VIEW;
import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.PackageManager.PERMISSION_GRANTED;
public class MediaDetailPagerFragment extends DaggerFragment implements ViewPager.OnPageChangeListener { public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPageChangeListener {
@Inject MediaWikiApi mwApi; @Inject MediaWikiApi mwApi;
@Inject SessionManager sessionManager; @Inject SessionManager sessionManager;
@ -85,6 +87,12 @@ public class MediaDetailPagerFragment extends DaggerFragment implements ViewPage
return view; return view;
} }
@Override
public void onAttach(Context context) {
AndroidSupportInjection.inject(this);
super.onAttach(context);
}
@Override @Override
public void onSaveInstanceState(Bundle outState) { public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);

View file

@ -12,13 +12,15 @@ import android.text.TextUtils;
import javax.inject.Inject; import javax.inject.Inject;
import dagger.Lazy;
import dagger.android.AndroidInjection; import dagger.android.AndroidInjection;
import fr.free.nrw.commons.data.DBOpenHelper; import fr.free.nrw.commons.data.DBOpenHelper;
import fr.free.nrw.commons.di.FixedDaggerContentProvider;
import timber.log.Timber; import timber.log.Timber;
import static fr.free.nrw.commons.modifications.ModifierSequenceDao.Table.TABLE_NAME; import static fr.free.nrw.commons.modifications.ModifierSequenceDao.Table.TABLE_NAME;
public class ModificationsContentProvider extends ContentProvider { public class ModificationsContentProvider extends FixedDaggerContentProvider {
private static final int MODIFICATIONS = 1; private static final int MODIFICATIONS = 1;
private static final int MODIFICATIONS_ID = 2; private static final int MODIFICATIONS_ID = 2;
@ -38,11 +40,11 @@ public class ModificationsContentProvider extends ContentProvider {
return Uri.parse(BASE_URI.toString() + "/" + id); return Uri.parse(BASE_URI.toString() + "/" + id);
} }
@Inject DBOpenHelper dbOpenHelper; @Inject Lazy<DBOpenHelper> dbOpenHelper;
@Override @Override
public boolean onCreate() { public boolean onCreate() {
AndroidInjection.inject(this); super.onCreate();
return true; return true;
} }
@ -60,7 +62,7 @@ public class ModificationsContentProvider extends ContentProvider {
throw new IllegalArgumentException("Unknown URI" + uri); throw new IllegalArgumentException("Unknown URI" + uri);
} }
SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); SQLiteDatabase db = dbOpenHelper.get().getReadableDatabase();
Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder); Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri); cursor.setNotificationUri(getContext().getContentResolver(), uri);
@ -76,7 +78,7 @@ public class ModificationsContentProvider extends ContentProvider {
@Override @Override
public Uri insert(@NonNull Uri uri, ContentValues contentValues) { public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
int uriType = uriMatcher.match(uri); int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); SQLiteDatabase sqlDB = dbOpenHelper.get().getWritableDatabase();
long id = 0; long id = 0;
switch (uriType) { switch (uriType) {
case MODIFICATIONS: case MODIFICATIONS:
@ -92,7 +94,7 @@ public class ModificationsContentProvider extends ContentProvider {
@Override @Override
public int delete(@NonNull Uri uri, String s, String[] strings) { public int delete(@NonNull Uri uri, String s, String[] strings) {
int uriType = uriMatcher.match(uri); int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); SQLiteDatabase sqlDB = dbOpenHelper.get().getWritableDatabase();
switch (uriType) { switch (uriType) {
case MODIFICATIONS_ID: case MODIFICATIONS_ID:
String id = uri.getLastPathSegment(); String id = uri.getLastPathSegment();
@ -110,7 +112,7 @@ public class ModificationsContentProvider extends ContentProvider {
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) { public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
Timber.d("Hello, bulk insert! (ModificationsContentProvider)"); Timber.d("Hello, bulk insert! (ModificationsContentProvider)");
int uriType = uriMatcher.match(uri); int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); SQLiteDatabase sqlDB = dbOpenHelper.get().getWritableDatabase();
sqlDB.beginTransaction(); sqlDB.beginTransaction();
switch (uriType) { switch (uriType) {
case MODIFICATIONS: case MODIFICATIONS:
@ -138,7 +140,7 @@ public class ModificationsContentProvider extends ContentProvider {
In here, the only concat created argument is for id. It is cast to an int, and will error out otherwise. In here, the only concat created argument is for id. It is cast to an int, and will error out otherwise.
*/ */
int uriType = uriMatcher.match(uri); int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase(); SQLiteDatabase sqlDB = dbOpenHelper.get().getWritableDatabase();
int rowsUpdated = 0; int rowsUpdated = 0;
switch (uriType) { switch (uriType) {
case MODIFICATIONS: case MODIFICATIONS:

View file

@ -20,6 +20,7 @@ import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.contributions.Contribution; import fr.free.nrw.commons.contributions.Contribution;
import fr.free.nrw.commons.contributions.ContributionDao; import fr.free.nrw.commons.contributions.ContributionDao;
import fr.free.nrw.commons.contributions.ContributionsContentProvider; import fr.free.nrw.commons.contributions.ContributionsContentProvider;
import fr.free.nrw.commons.di.ApplicationlessInjection;
import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.mwapi.MediaWikiApi;
import timber.log.Timber; import timber.log.Timber;
@ -36,7 +37,11 @@ public class ModificationsSyncAdapter extends AbstractThreadedSyncAdapter {
@Override @Override
public void onPerformSync(Account account, Bundle bundle, String s, ContentProviderClient contentProviderClient, SyncResult syncResult) { public void onPerformSync(Account account, Bundle bundle, String s, ContentProviderClient contentProviderClient, SyncResult syncResult) {
// This code is fraught with possibilities of race conditions, but lalalalala I can't hear you! // This code is fraught with possibilities of race conditions, but lalalalala I can't hear you!
((CommonsApplication)getContext().getApplicationContext()).injector().inject(this); ApplicationlessInjection
.getInstance(getContext()
.getApplicationContext())
.getCommonsApplicationComponent()
.inject(this);
Cursor allModifications; Cursor allModifications;
try { try {

View file

@ -1,7 +1,9 @@
package fr.free.nrw.commons.nearby; package fr.free.nrw.commons.nearby;
import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -16,13 +18,14 @@ import java.lang.reflect.Type;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import dagger.android.support.AndroidSupportInjection;
import dagger.android.support.DaggerFragment; import dagger.android.support.DaggerFragment;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.location.LatLng; import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.utils.UriDeserializer; import fr.free.nrw.commons.utils.UriDeserializer;
import timber.log.Timber; import timber.log.Timber;
public class NearbyListFragment extends DaggerFragment { public class NearbyListFragment extends Fragment {
private static final Type LIST_TYPE = new TypeToken<List<Place>>() { private static final Type LIST_TYPE = new TypeToken<List<Place>>() {
}.getType(); }.getType();
private static final Type CUR_LAT_LNG_TYPE = new TypeToken<LatLng>() { private static final Type CUR_LAT_LNG_TYPE = new TypeToken<LatLng>() {
@ -40,6 +43,12 @@ public class NearbyListFragment extends DaggerFragment {
setRetainInstance(true); setRetainInstance(true);
} }
@Override
public void onAttach(Context context) {
AndroidSupportInjection.inject(this);
super.onAttach(context);
}
@Override @Override
public View onCreateView(LayoutInflater inflater, public View onCreateView(LayoutInflater inflater,
ViewGroup container, ViewGroup container,

View file

@ -1,11 +1,14 @@
package fr.free.nrw.commons.nearby; package fr.free.nrw.commons.nearby;
import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import dagger.android.support.AndroidSupportInjection;
import dagger.android.support.DaggerFragment; import dagger.android.support.DaggerFragment;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import timber.log.Timber; import timber.log.Timber;
@ -13,11 +16,17 @@ import timber.log.Timber;
/** /**
* Tells user that Nearby Places cannot be displayed if location permissions are denied * Tells user that Nearby Places cannot be displayed if location permissions are denied
*/ */
public class NoPermissionsFragment extends DaggerFragment { public class NoPermissionsFragment extends Fragment {
public NoPermissionsFragment() { public NoPermissionsFragment() {
} }
@Override
public void onAttach(Context context) {
AndroidSupportInjection.inject(this);
super.onAttach(context);
}
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {

View file

@ -30,6 +30,7 @@ import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.di.ApplicationlessInjection;
import fr.free.nrw.commons.utils.FileUtils; import fr.free.nrw.commons.utils.FileUtils;
public class SettingsFragment extends PreferenceFragment { public class SettingsFragment extends PreferenceFragment {
@ -40,8 +41,11 @@ public class SettingsFragment extends PreferenceFragment {
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ApplicationlessInjection
.getInstance(getActivity().getApplicationContext())
.getCommonsApplicationComponent()
.inject(this);
// Load the preferences from an XML resource // Load the preferences from an XML resource
addPreferencesFromResource(R.xml.preferences); addPreferencesFromResource(R.xml.preferences);

View file

@ -3,11 +3,14 @@ package fr.free.nrw.commons.theme;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import dagger.android.AndroidInjection;
import dagger.android.support.DaggerAppCompatActivity; import dagger.android.support.DaggerAppCompatActivity;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.di.FixedDaggerAppCompatActivity;
public abstract class BaseActivity extends DaggerAppCompatActivity { public abstract class BaseActivity extends FixedDaggerAppCompatActivity {
boolean currentTheme; boolean currentTheme;
@Override @Override

View file

@ -5,6 +5,7 @@ import android.graphics.Point;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.support.graphics.drawable.VectorDrawableCompat; import android.support.graphics.drawable.VectorDrawableCompat;
import android.support.v4.app.Fragment;
import android.text.Editable; import android.text.Editable;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.TextWatcher; import android.text.TextWatcher;
@ -27,12 +28,12 @@ import android.widget.TextView;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder; import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.view.SimpleDraweeView; import com.facebook.drawee.view.SimpleDraweeView;
import dagger.android.support.DaggerFragment; import dagger.android.support.AndroidSupportInjection;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.contributions.Contribution; import fr.free.nrw.commons.contributions.Contribution;
import fr.free.nrw.commons.media.MediaDetailPagerFragment; import fr.free.nrw.commons.media.MediaDetailPagerFragment;
public class MultipleUploadListFragment extends DaggerFragment { public class MultipleUploadListFragment extends Fragment {
public interface OnMultipleUploadInitiatedHandler { public interface OnMultipleUploadInitiatedHandler {
void OnMultipleUploadInitiated(); void OnMultipleUploadInitiated();
@ -56,6 +57,12 @@ public class MultipleUploadListFragment extends DaggerFragment {
private RelativeLayout overlay; private RelativeLayout overlay;
} }
@Override
public void onAttach(Context context) {
AndroidSupportInjection.inject(this);
super.onAttach(context);
}
private class PhotoDisplayAdapter extends BaseAdapter { private class PhotoDisplayAdapter extends BaseAdapter {
@Override @Override

View file

@ -8,6 +8,7 @@ import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
@ -36,16 +37,18 @@ import butterknife.ButterKnife;
import butterknife.OnClick; import butterknife.OnClick;
import butterknife.OnItemSelected; import butterknife.OnItemSelected;
import butterknife.OnTouch; import butterknife.OnTouch;
import dagger.android.support.AndroidSupportInjection;
import dagger.android.support.DaggerFragment; import dagger.android.support.DaggerFragment;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.di.FixedDaggerFragment;
import fr.free.nrw.commons.settings.Prefs; import fr.free.nrw.commons.settings.Prefs;
import timber.log.Timber; import timber.log.Timber;
import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP; import static android.view.MotionEvent.ACTION_UP;
public class SingleUploadFragment extends DaggerFragment { public class SingleUploadFragment extends FixedDaggerFragment {
@BindView(R.id.titleEdit) EditText titleEdit; @BindView(R.id.titleEdit) EditText titleEdit;
@BindView(R.id.descEdit) EditText descEdit; @BindView(R.id.descEdit) EditText descEdit;
@ -67,6 +70,11 @@ public class SingleUploadFragment extends DaggerFragment {
} }
} }
@Override
public void onAttach(Context context) {
super.onAttach(context);
}
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {

View file

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="crash_dialog_title">Ostava se srušila</string>
<string name="crash_dialog_text">Ups! Nešto je pošlo naopako.</string>
<string name="crash_dialog_comment_prompt">Recite nam šta ste radili pa to saznanje podelite s nama, putem e-pošte. Time ćete nam pomoći da rešimo problem!</string>
<string name="crash_dialog_ok_toast">Hvala vam!</string>
</resources>

View file

@ -1,208 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Ostava</string>
<string name="menu_settings">Podešavanja</string>
<string name="username">Korisničko ime</string>
<string name="password">Lozinka</string>
<string name="login">Prijavi me</string>
<string name="signup">Otvori nalog</string>
<string name="logging_in_title">Prijavljivanje</string>
<string name="logging_in_message">Sačekajte…</string>
<string name="login_success">Uspešno ste prijavljeni.</string>
<string name="login_failed">Prijavljivanje nije uspelo.</string>
<string name="upload_failed">Datoteka nije pronađena. Pokušajte sa drugom datotekom.</string>
<string name="authentication_failed">Provera identiteta nije uspela.</string>
<string name="uploading_started">Otpremanje je započeto.</string>
<string name="upload_completed_notification_title">Datoteka „%1$s“ je otpremljena.</string>
<string name="upload_completed_notification_text">Tapnite da biste videli otpremanje</string>
<string name="upload_progress_notification_title_start">Počinjem sa otpremanjem datoteke „%1$s“</string>
<string name="upload_progress_notification_title_in_progress">Otpremanje datoteke „%1$s“</string>
<string name="upload_progress_notification_title_finishing">Završavam sa otpremanjem datoteke „%1$s“</string>
<string name="upload_failed_notification_title">Ne mogu da otpremim „%1$s“</string>
<string name="upload_failed_notification_subtitle">Tapnite da biste videli</string>
<plurals name="uploads_pending_notification_indicator">
<item quantity="one">%d datoteka se otprema</item>
<item quantity="other">%d datoteke se otpremaju</item>
</plurals>
<string name="title_activity_contributions">Moja skorašnja otpremanja</string>
<string name="contribution_state_queued">Na čekanju</string>
<string name="contribution_state_failed">Nije uspelo</string>
<string name="contribution_state_in_progress">%1$d%% otpremljeno</string>
<string name="contribution_state_starting">Otpremam</string>
<string name="menu_from_gallery">Iz galerije</string>
<string name="menu_from_camera">Fotografiši</string>
<string name="menu_nearby">U blizini</string>
<string name="provider_contributions">Moja otpremanja</string>
<string name="menu_share">Deli</string>
<string name="menu_open_in_browser">Otvori u pregledaču</string>
<string name="share_title_hint">Naslov</string>
<string name="share_description_hint">Opis</string>
<string name="login_failed_network">Ne mogu da vas prijavim mreža ne radi</string>
<string name="login_failed_username">Ne mogu da vas prijavim proverite svoje korisničko ime</string>
<string name="login_failed_password">Ne mogu da vas prijavim proverite svoju lozinku</string>
<string name="login_failed_throttled">Previše neuspešnih pokušaja. Probajte ponovo za nekoliko minuta.</string>
<string name="login_failed_blocked">Nažalost, korisnik je blokiran na Ostavi</string>
<string name="login_failed_2fa_needed">Morate uneti svoj dvofaktorski kod za autentifikaciju.</string>
<string name="login_failed_generic">Prijava nije uspela</string>
<string name="share_upload_button">Otpremi</string>
<string name="multiple_share_base_title">Dajte ime ovom kompletu</string>
<string name="provider_modifications">Izmene</string>
<string name="menu_upload_single">Otpremi</string>
<string name="categories_search_text_hint">Pretraži kategorije</string>
<string name="menu_save_categories">Sačuvaj</string>
<string name="refresh_button">Osveži</string>
<string name="gps_disabled">GPS je onemogućen na Vašem uređaju. Želite li ga omogućiti?</string>
<string name="enable_gps">Omogući GPS</string>
<string name="contributions_subtitle_zero">Još uvek nema otpremanja</string>
<plurals name="contributions_subtitle">
<item quantity="zero">\@string/contributions_subtitle_zero</item>
<item quantity="one">%d otpremanje</item>
<item quantity="other">%d otpremanja</item>
</plurals>
<plurals name="starting_multiple_uploads">
<item quantity="one">Započni %d otpremanje</item>
<item quantity="other">Započni %d otpremanja</item>
</plurals>
<plurals name="multiple_uploads_title">
<item quantity="one">%d otpremanje</item>
<item quantity="other">%d otpremanja</item>
</plurals>
<string name="categories_not_found">Nema kategorija koje odgovaraju %1$s</string>
<string name="categories_skip_explanation">Dodajte kategorije na slike da biste olakšali korisnicima njihovo pronalaženje na Ostavi.\n\nDa biste dodali kategoriju, počnite sa pisanjem njenog imena.</string>
<string name="categories_activity_title">Kategorije</string>
<string name="title_activity_settings">Postavke</string>
<string name="title_activity_signup">Otvori nalog</string>
<string name="menu_about">O aplikaciji</string>
<string name="about_license">Softver otvorenog koda dostupan pod licencom &lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\"&gt;Apache ver. 2&lt;/a&gt; Vikimedijina Ostava i njen logo su zaštitni znaci Vikimedijine Fondacije i koriste se sa dozvolom Vikimedijine Fondacine. Mi ne odobravamo ili podržavmo Vikimedijinu Fondaciju.\n\nAplikacija za Vikimedijinu ostavu je aplikacija otvorenog koda koja je napravljena i koja se održava pomoću grantova i volontera Vikimedijine zajednice. Zadužbina Vikimedija nije uključena u stvaranje, razvoj ili održavanje aplikacije.</string>
<string name="about_improve">&lt;a href=\"https://github.com/commons-app/apps-android-commons\"&gt;Izvorni kôd&lt;/a&gt; i &lt;a href=\"https://commons-app.github.io/\"&gt;veb-sajt&lt;/a&gt; na GitHub-u. Napravite novi &lt;a href=\"https://github.com/commons-app/apps-android-commons/issues\"&gt;zahtev na GitHub-u&lt;/a&gt; da biste prijavili greške ili dali predloge.</string>
<string name="about_privacy_policy">&lt;a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\"&gt;Politika privatnosti&lt;/a&gt;</string>
<string name="about_credits">&lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\"&gt;Zasluge&lt;/a&gt;</string>
<string name="title_activity_about">O aplikaciji</string>
<string name="menu_feedback">Pošaljite povratne informacije (putem e-pošte)</string>
<string name="no_email_client">Nije instaliran imejl klijent</string>
<string name="provider_categories">Nedavno korišćene kategorije</string>
<string name="waiting_first_sync">Čekam na prvu sinhronizaciju…</string>
<string name="no_uploads_yet">Još niste otpremili nijednu fotografiju.</string>
<string name="menu_retry_upload">Pokušaj ponovo</string>
<string name="menu_cancel_upload">Otkaži</string>
<string name="share_license_summary">Slika će se voditi pod licencom %1$s</string>
<string name="media_upload_policy">Slanjem ove slike, ja tvrdim da je u pitanju moj rad, da ne sadrži materijal ili selfije zaštićene autorskim pravima, te da je na ostale načine u skladu sa &lt;a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\"&gt;smernicama Vikimedijine ostave&lt;/a&gt;.</string>
<string name="menu_download">Preuzmi</string>
<string name="preference_license">Licenca</string>
<string name="use_previous">Koristi prethodan naslov/opis</string>
<string name="allow_gps">Automatski detektuj trenutnu lokaciju</string>
<string name="allow_gps_summary">Primi trenutnu lokaciju da bi predložili kategoriju ako slika nije geografski označena</string>
<string name="preference_theme">Noćni režim</string>
<string name="preference_theme_summary">Koristiti tamnu temu</string>
<string name="license_name_cc_by_sa_four">Autorstvo-Deliti pod istim uslovima 4.0</string>
<string name="license_name_cc_by_four">Autorstvo 4.0</string>
<string name="license_name_cc_by_sa">Autorstvo-Deliti pod istim uslovimau 3.0</string>
<string name="license_name_cc_by">Autorstvo 3.0</string>
<string name="license_name_cc0">CC0</string>
<string name="license_name_cc_by_sa_3_0">CC BY-SA 3.0</string>
<string name="license_name_cc_by_sa_3_0_at">CC BY-SA 3.0 (Austrija)</string>
<string name="license_name_cc_by_sa_3_0_de">CC BY-SA 3.0 (Nemačka)</string>
<string name="license_name_cc_by_sa_3_0_ee">CC BY-SA 3.0 (Estonija)</string>
<string name="license_name_cc_by_sa_3_0_es">CC BY-SA 3.0 (Španija)</string>
<string name="license_name_cc_by_sa_3_0_hr">CC BY-SA 3.0 (Hrvatska)</string>
<string name="license_name_cc_by_sa_3_0_lu">CC BY-SA 3.0 (Luksemburg)</string>
<string name="license_name_cc_by_sa_3_0_nl">CC BY-SA 3.0 (Holandija)</string>
<string name="license_name_cc_by_sa_3_0_no">CC BY-SA 3.0 (Norveška)</string>
<string name="license_name_cc_by_sa_3_0_pl">CC BY-SA 3.0 (Poljska)</string>
<string name="license_name_cc_by_sa_3_0_ro">CC BY-SA 3.0 (Rumunija)</string>
<string name="license_name_cc_by_3_0">CC BY 3.0</string>
<string name="license_name_cc_by_sa_4_0">CC BY-SA 4.0</string>
<string name="license_name_cc_by_4_0">CC BY 4.0</string>
<string name="license_name_cc_zero">CC Nula</string>
<string name="tutorial_1_text">Vikimedijina Ostava sadrži većinu slika koja se koristi na Vikipediji.</string>
<string name="tutorial_1_subtext">Vaše slike pomažu u obrazovanju ljudi širom sveta.</string>
<string name="tutorial_2_text">Molimo Vas da postavite slike koje ste preuzeli ili kreirali u potpunosti sami:</string>
<string name="tutorial_2_subtext">- Prirodne objekte (cveće, životinje, planine)\n- Korisne objekte (bicikle, železničke stanice)\n- Poznate ljude (vaš gradonačelnik, Olimpijce koje ste sreli)</string>
<string name="tutorial_3_text">Molimo NE otpremajte:</string>
<string name="tutorial_3_subtext">- Selfije i slike tvojih prijatelja\n- Slike koje ste preuzeli sa interneta\n- Skrinšotove iz sopstvenih aplikacija</string>
<string name="tutorial_4_text">Primer otpremanja:</string>
<string name="tutorial_4_subtext">— Naslov: Sidnejska opera\n— Opis: Sidnejska opera, pogled preko zaliva\n— Kategorije: Sidnejska opera sa zapada, pogledi na Sidnejksu operu iz daljine</string>
<string name="welcome_wikipedia_text">Delite svoje slike. Oživite članke na Vikipediji!</string>
<string name="welcome_wikipedia_subtext">Slike na Vikipediji dolaze iz Ostave.</string>
<string name="welcome_copyright_text">Sa vašim slikama pomažete u obrazovanju ljudi širom sveta.</string>
<string name="welcome_copyright_subtext">Izbegavajte materijale koje ste našli na internetu, kao i slike plakata, korica knjiga itd.</string>
<string name="welcome_final_text">Jeste li razumeli?</string>
<string name="welcome_final_button_text">Jesam!</string>
<string name="detail_panel_cats_label">Kategorije</string>
<string name="detail_panel_cats_loading">Učitavam…</string>
<string name="detail_panel_cats_none">Ništa nije izabrano</string>
<string name="detail_description_empty">Nema opisa</string>
<string name="detail_license_empty">Nepoznata licenca</string>
<string name="menu_refresh">Osveži</string>
<string name="read_storage_permission_rationale">Potrebna dozvola: Provera spoljašnje memorije. Aplikacija bez ovoga ne može da funkcioniše.</string>
<string name="write_storage_permission_rationale">Neophodna dozvola: Pisanje spoljašnjeg skladišta. Aplikacija ne može da funkcioniše bez ovoga.</string>
<string name="location_permission_rationale">Opciona dozvola: Preuzmi trenutnu lokaciju za predloge kategorija</string>
<string name="ok">U redu</string>
<string name="title_activity_nearby">Mesta u blizini</string>
<string name="no_nearby">Nisu pronađena obližnja mesta</string>
<string name="warning">Upozorenje</string>
<string name="file_exists">Ova datoteka je već dostupna na Ostavi. Da li ste sigurni da želite da nastavite?</string>
<string name="yes">Da</string>
<string name="no">Ne</string>
<string name="media_detail_title">Naslov</string>
<string name="media_detail_media_title">Naslov medija</string>
<string name="media_detail_description">Opis</string>
<string name="media_detail_description_explanation">Opis datoteke ide ovde. Može da bude poprilično dug i prikazivaće se u više redova. Nadamo se da će izgledati lepo.</string>
<string name="media_detail_uploaded_date">Datum otpremanja</string>
<string name="media_detail_license">Licenca</string>
<string name="media_detail_coordinates">Koordinate</string>
<string name="media_detail_coordinates_empty">Ništa nije uneto</string>
<string name="become_a_tester_title">Postani Beta Tester</string>
<string name="become_a_tester_description">Priključite se na naš beta kanal na Gugl pleju i pristupajte novim informacijama i popravkama bagova</string>
<string name="use_wikidata">Koristi Vikipodatke</string>
<string name="use_wikidata_summary">(Upozorenje: onemogućavanjem ovoga može se izazvati velika potrošnja mobilnih podataka)</string>
<string name="_2fa_code">2FA kod</string>
<string name="number_of_uploads">Moj limit za skorašnja otpremanja</string>
<string name="maximum_limit">Maksimalni limit</string>
<string name="maximum_limit_alert">Nije moguće prikazati više od 500</string>
<string name="set_limit">Postavi limit za skorašnja otpremanja</string>
<string name="login_failed_2fa_not_supported">Dvofaktorska autentifikacija trenutno nije podržana.</string>
<string name="logout_verification">Zaista želite da se odjavite?</string>
<string name="commons_logo">Logo Ostave</string>
<string name="background_image">Pozadinska slika</string>
<string name="mediaimage_failed">Medijska slika neuspešna</string>
<string name="no_image_found">Slika nije pronađena</string>
<string name="upload_image">Otpremi sliku</string>
<string name="welcome_image_mount_zao">Planina Zao</string>
<string name="welcome_image_llamas">Lame</string>
<string name="welcome_image_rainbow_bridge">Dugin most</string>
<string name="welcome_image_tulip">Tulipan</string>
<string name="welcome_image_no_selfies">Bez selfija</string>
<string name="welcome_image_proprietary">Vlasnička slika</string>
<string name="welcome_image_welcome_wikipedia">Dobrodošlica Vikipediji</string>
<string name="welcome_image_welcome_copyright">Dobrodošlica za autorska prava</string>
<string name="welcome_image_sydney_opera_house">Sidnejska opera</string>
<string name="cancel">Otkaži</string>
<string name="navigation_drawer_open">Otvori</string>
<string name="navigation_drawer_close">Zatvori</string>
<string name="navigation_item_home">Početna</string>
<string name="navigation_item_upload">Otpremanje</string>
<string name="navigation_item_nearby">U blizini</string>
<string name="navigation_item_about">O nama</string>
<string name="navigation_item_settings">Podešavanja</string>
<string name="navigation_item_feedback">Povratne informacije</string>
<string name="navigation_item_logout">Odjavi me</string>
<string name="navigation_item_info">Tutorijal</string>
<string name="navigation_item_notification">Obaveštenja</string>
<string name="nearby_needs_permissions">Obližnja mesta ne mogu da se prikazuju bez dozvola za lokaciju</string>
<string name="no_description_found">opis nije pronađen</string>
<string name="nearby_info_menu_commons_article">Stranica datoteke na Ostavi</string>
<string name="nearby_info_menu_wikidata_article">Stavka na Vikipodacima</string>
<string name="error_while_cache">Greška pri keširanju slika</string>
<string name="title_info">Jedinstven opisni naslov za datoteku, koji će biti ime datoteke. Možete da koristite obični jezik sa razmacima. Ne treba unositi ekstenziju datoteke</string>
<string name="description_info">Molimo da opišete datoteku koliko je to moguće: Gde je napravljena? Šta prikazuje? Šta je kontekst? Opišite objekte i/ili osobe. Otkrijte informacije koje se ne mogu lako pogoditi, na primer doba dana ako je u pitanju pejzaž. Ako datoteka prikazuje nešto neobično, molimo da objasnite šta je to čini neobičnom.</string>
<string name="give_permission">Davanje dozvole</string>
<string name="use_external_storage">Upotreba spoljašnjeg skladišta</string>
<string name="use_external_storage_summary">Spremanje slika napravljenih kamerom aplikacije na Vašem uređaju</string>
<string name="send_log_file">Pošalji dnevničku datoteku</string>
<string name="send_log_file_description">Pošalji dnevničku datoteku developerima preko imejla</string>
<string name="login_to_your_account">Prijavite se na svoj nalog</string>
<string name="nearby_location_has_not_changed">Lokacija nije promenjena.</string>
<string name="nearby_location_not_available">Lokacija nije dostupna.</string>
<string name="location_permission_rationale_nearby">Potrebna je dozvola za prikazivanje liste lokacija u blizini</string>
</resources>