Reputation: 41
i have problem went i try to implement content provider, in my comsumer apps :
This is my Android Manifest from App A (Provider) :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.erlanggakmoekasan.imovie">
<permission android:name="com.erlanggakmoekasan.imovie.READ_DATABASE" android:protectionLevel="normal" />
<permission android:name="com.erlanggakmoekasan.imovie.WRITE_DATABASE" android:protectionLevel="normal" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true">
<receiver android:name=".widgets.MoviesWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/movies_widget_info" />
</receiver>
<activity android:name=".activity.TvShowActivitySearch" />
<activity android:name=".activity.MovieActivitySearch" />
<activity android:name=".activity.TvShowActivity" />
<activity
android:name=".activity.MainActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".activity.MovieActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.MainActivity" />
</activity>
<provider
android:authorities="com.erlanggakmoekasan.imovie"
android:name=".provider.MoviesProvider"
android:exported="true"
android:readPermission="com.erlanggakmoekasan.imovie.READ_DATABASE"
android:writePermission="com.erlanggakmoekasan.imovie.WRITE_DATABASE" />
<service
android:name="com.erlanggakmoekasan.imovie.widgets.StackWidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS" />
</application>
</manifest>
This is my Android Manifest from App B (Consumer) :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.erlanggakmoekasan.favoritemovies">
<uses-permission android:name="android.permission.INTERNET"/>
<permission android:name="com.erlanggakmoekasan.imovie.READ_DATABASE" />
<permission android:name="com.erlanggakmoekasan.imovie.WRITE_DATABASE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".activity.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
My Provider from App A :
package com.erlanggakmoekasan.imovie.provider;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.Nullable;
import com.erlanggakmoekasan.imovie.utils.MoviesHelper;
import static com.erlanggakmoekasan.imovie.utils.MoviesContract.AUTHORITY;
import static com.erlanggakmoekasan.imovie.utils.MoviesContract.MoviesColumns.CONTENT_URI;
import static com.erlanggakmoekasan.imovie.utils.MoviesContract.MoviesColumns.TABLE_NAME;
public class MoviesProvider extends ContentProvider {
private static final int MOVIES = 1;
private static final int MOVIES_ID = 2;
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
// content://com.erlanggastudio.imovie/movies
sUriMatcher.addURI(AUTHORITY, TABLE_NAME, MOVIES);
// content://com.erlanggastudio.imovie/movies/id
sUriMatcher.addURI(AUTHORITY,
TABLE_NAME+ "/#",
MOVIES_ID);
}
private MoviesHelper moviesHelper;
@Override
public boolean onCreate() {
moviesHelper = MoviesHelper.getInstance(getContext());
return false;
}
@Nullable
@Override
public Cursor query(@Nullable Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
moviesHelper.open();
Cursor cursor;
switch (sUriMatcher.match(uri)) {
case MOVIES:
cursor = moviesHelper.queryProvider();
break;
case MOVIES_ID:
cursor = moviesHelper.queryByIdProvider(uri.getLastPathSegment());
break;
default:
cursor = null;
break;
}
if (cursor!=null){
cursor.setNotificationUri(getContext().getContentResolver(),uri);
}
return cursor;
}
@Nullable
@Override
public String getType(@Nullable Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@Nullable Uri uri, @Nullable ContentValues values) {
moviesHelper.open();
long added ;
switch (sUriMatcher.match(uri)){
case MOVIES:
added = moviesHelper.insertProvider(values);
break;
default:
added = 0;
break;
}
if (added > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return Uri.parse(CONTENT_URI + "/" + added);
}
@Nullable
public int delete(@Nullable Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
moviesHelper.open();
int deleted;
switch (sUriMatcher.match(uri)) {
case MOVIES_ID:
deleted = moviesHelper.deleteProvider(uri.getLastPathSegment());
break;
default:
deleted = 0;
break;
}
if (deleted > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return deleted;
}
@Nullable
public int update(@Nullable Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
moviesHelper.open();
int movieUpdated ;
switch (sUriMatcher.match(uri)) {
case MOVIES_ID:
movieUpdated = moviesHelper.updateProvider(uri.getLastPathSegment(),values);
break;
default:
movieUpdated = 0;
break;
}
if (movieUpdated > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return movieUpdated;
}
}
My Main Activity from App B :
package com.erlanggakmoekasan.favoritemovies.activity;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.Toast;
import com.erlanggakmoekasan.favoritemovies.R;
import com.erlanggakmoekasan.favoritemovies.adapter.MoviesAdapter;
import com.erlanggakmoekasan.favoritemovies.callback.MoviesCallback;
import com.erlanggakmoekasan.favoritemovies.model.Movie;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import static com.erlanggakmoekasan.favoritemovies.entity.MappingHelper.mapCursorToArrayList;
import static com.erlanggakmoekasan.favoritemovies.utils.MoviesContract.MoviesColumns.CONTENT_URI;
public class MainActivity extends AppCompatActivity implements MoviesCallback {
private DataObserver myObserver;
private RecyclerView moviesList;
private MoviesAdapter moviesAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
moviesList = findViewById(R.id.movie_list);
moviesAdapter = new MoviesAdapter(this);
moviesList.setLayoutManager(new LinearLayoutManager(this));
moviesList.setHasFixedSize(true);
moviesList.setAdapter(moviesAdapter);
HandlerThread handlerThread = new HandlerThread("DataObserver");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
myObserver = new DataObserver(handler, this);
getContentResolver().registerContentObserver(CONTENT_URI, true, myObserver);
new getData(this, this).execute();
}
@Override
public void postExecute(Cursor movie) {
ArrayList<Movie> listMovie = mapCursorToArrayList(movie);
if (listMovie.size() > 0) {
moviesAdapter.setListMovies(listMovie);
} else {
Toast.makeText(this, "Tidak Ada data saat ini", Toast.LENGTH_SHORT).show();
moviesAdapter.setListMovies(new ArrayList<Movie>());
}
}
private static class getData extends AsyncTask<Void, Void, Cursor> {
private final WeakReference<Context> weakContext;
private final WeakReference<MoviesCallback> weakCallback;
private getData(Context context, MoviesCallback callback) {
weakContext = new WeakReference<>(context);
weakCallback = new WeakReference<>(callback);
}
@Override
protected Cursor doInBackground(Void... voids) {
return weakContext.get().getContentResolver().query(CONTENT_URI, null, null, null, null);
}
@Override
protected void onPostExecute(Cursor data) {
super.onPostExecute(data);
weakCallback.get().postExecute(data);
}
}
static class DataObserver extends ContentObserver {
final Context context;
DataObserver(Handler handler, Context context) {
super(handler);
this.context = context;
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
new getData(context, (MainActivity) context).execute();
}
}
}
The error is :
Process: com.erlanggakmoekasan.favoritemovies, PID: 27146 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.erlanggakmoekasan.favoritemovies/com.erlanggakmoekasan.favoritemovies.activity.MainActivity}: java.lang.SecurityException: Permission Denial: opening provider com.erlanggakmoekasan.imovie.provider.MoviesProvider from ProcessRecord{7b99e1ed0 27146:com.erlanggakmoekasan.favoritemovies/u0a307} (pid=27146, uid=10307) requires com.erlanggakmoekasan.imovie.READ_DATABASE or com.erlanggakmoekasan.imovie.WRITE_DATABASE
the error when trying to call getContentResolver().registerContentObserver(CONTENT_URI, true, myObserver);
from app B Any solution for my problem???
Upvotes: 2
Views: 3647
Reputation: 1006849
App B needs <uses-permission>
elements for com.erlanggakmoekasan.imovie.READ_DATABASE
or com.erlanggakmoekasan.imovie.WRITE_DATABASE
.
Also, you need to ensure that either:
App A is always installed before App B, or
App B has identical <permission>
elements as App A and both App A and App B are signed by the same signing key
Custom permissions do not work all that well in Android. If possible, combine these two apps into a single app.
Upvotes: 4