如何将相机应用中的照片保存到公共目录中

huangapple go评论69阅读模式
英文:

How to save photo Camera-App in Public directory

问题

以下是您提供的代码的翻译部分:

package pub.openbook.labellor;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;

import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.FileProvider;

import android.os.Environment;
import android.provider.MediaStore;
import android.view.View;

import android.view.Menu;
import android.view.MenuItem;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.util.Log;
import android.widget.LinearLayout;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;

import static android.os.Environment.getExternalStoragePublicDirectory;

public class MainActivity extends AppCompatActivity {

    static final int REQUEST_IMAGE_CAPTURE = 1;
    static final int REQUEST_TAKE_PHOTO = 1;
    static final boolean USE_ANDROID_EXTERNAL_STORAGE_PUBLIC_DIRECTORY = true;

    private static final String IMAGES_FOLDER_NAME = "Camera";
    String stPathToJpgFile;

    View mainCoordinatorLayout;
    private static final String logTag = MainActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 初始化布局:
        setContentView(R.layout.activity_main);
        // 初始化变量:
        mainCoordinatorLayout = (View) findViewById(R.id.main_coordinator_layout);
        Toolbar toolbar = findViewById(R.id.toolbar);
        FloatingActionButton fab = findViewById(R.id.fab);
        // 初始化布局和组件:
        setSupportActionBar(toolbar);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                dispatchTakePictureIntent(view);
            }
        });

        LinearLayout checkboxContainer = (LinearLayout) findViewById(R.id.checkbox_container);
        CheckBox cb = new CheckBox(this);
        cb.setText("Tutlane");
        cb.setChecked(true);
        checkboxContainer.addView(cb);
        cb = new CheckBox(this);
        cb.setText("Another");
        cb.setChecked(false);
        checkboxContainer.addView(cb);
        cb = new CheckBox(this);
        cb.setText("Label threee");
        cb.setChecked(false);
        checkboxContainer.addView(cb);

        Log.d(logTag, "============ android.os.Build.VERSION.SDK_INT " + android.os.Build.VERSION.SDK_INT + "=====================");
    }

    // ... 其余代码未翻译 ...
}

由于篇幅较长,以上是代码中一部分的翻译。如果您需要完整的翻译,请继续告知我。

英文:

I would like to achieve the following:

  1. From MyApp, start the phone's Camera app via an intent
  2. Phone's Camera app saves photo in a public directory (DCIM or DCIM/Camera)
  3. Saved photo has a filename of my choosing.
  4. Saved photo is available in Gallery App
  5. My app edits the Exif after Camera App return focus to MyApp.

In my MainActivity, I use a boolean USE_ANDROID_EXTERNAL_STORAGE_PUBLIC_DIRECTORY to switch between:

  • Saving photo-file to ExternalFilesDir (in my case: /storage/emulated/0/Android/data/pub.openbook.labellor/files/Pictures)
  • Saving photo-file to Public Directory (in my case /storage/emulated/0/DCIM)

The Camera app will successfully save the photo-file in ExternalFilesDir, but will fail to save the file to its Public Directory. As this happens within the Camera App, I am unable to debug this.

My questions:

  • Is it even possible to have the Camera App save to Public Directory DCIM?
  • If so, how?
  • How do I make my the photo visible in the Gallery App?

(My function galleryAddPic() completes without crash, but does not achieve it purpose. The photo remains invisible in the Gallery App.)

I am working with:

  • AndroidStudio 4.0.1
  • SDK platform Android 10.0+(R), API 30
  • My test device has android.os.Build.VERSION.SDK_INT 23
package pub.openbook.labellor;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.FileProvider;
import android.os.Environment;
import android.provider.MediaStore;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.util.Log;
import android.widget.LinearLayout;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import static android.os.Environment.getExternalStoragePublicDirectory;
public class MainActivity extends AppCompatActivity {
static final int REQUEST_IMAGE_CAPTURE = 1;
static final int REQUEST_TAKE_PHOTO = 1;
static final boolean USE_ANDROID_EXTERNAL_STORAGE_PUBLIC_DIRECTORY = true;
private static final String IMAGES_FOLDER_NAME = "Camera";
String stPathToJpgFile;
View mainCoordinatorLayout;
private static final String logTag = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// initialize layout:
setContentView(R.layout.activity_main);
// initialize variables:
mainCoordinatorLayout = (View) findViewById(R.id.main_coordinator_layout);
Toolbar toolbar = findViewById(R.id.toolbar);
FloatingActionButton fab = findViewById(R.id.fab);
// initialize layout & and components:
setSupportActionBar(toolbar);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dispatchTakePictureIntent(view);
}
});
LinearLayout checkboxContainer = (LinearLayout) findViewById(R.id.checkbox_container);
CheckBox cb = new CheckBox(this);
cb.setText("Tutlane");
cb.setChecked(true);
checkboxContainer.addView(cb);
cb = new CheckBox(this);
cb.setText("Another");
cb.setChecked(false);
checkboxContainer.addView(cb);
cb = new CheckBox(this);
cb.setText("Label threee");
cb.setChecked(false);
checkboxContainer.addView(cb);
Log.d(logTag, "============ android.os.Build.VERSION.SDK_INT " + android.os.Build.VERSION.SDK_INT + "=====================");
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.d(logTag, "onActivityResult() back from take picture intent;");
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
Log.d(logTag, "onActivityResult() photo resides at"+stPathToJpgFile);
galleryAddPic(stPathToJpgFile);
ImageView imageView = (ImageView) findViewById(R.id.thumbnail_view);
Context mContext;
mContext = (Context)this;
Bitmap d = new BitmapDrawable(mContext.getResources() , stPathToJpgFile).getBitmap();
if (null != d) {
int nh = (int) ( d.getHeight() * (512.0 / d.getWidth()) );
Bitmap scaled = Bitmap.createScaledBitmap(d, 512, nh, true);
imageView.setImageBitmap(scaled);
}
}
}
/*
Use resident Camera App to take picture and save (filename has labels)
*/
private void dispatchTakePictureIntent(View view) {
// take picture with resident camera app:
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = jpgFile();
} catch (IOException ex) {
// Error occurred while creating the File
Log.e(logTag, "jpgFile() throws IOException");
}
Log.d(logTag, "dispatchTakePictureIntent() photoFile = "+photoFile );
// Continue only if the File was successfully created
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this,
BuildConfig.APPLICATION_ID + ".provider",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
}
}
}
/*
*/
private File jpgFile() throws IOException {
// File object to be returned:
File jpgFile;
// Create an image file name with datestamp and labels:
String timeStamp = new SimpleDateFormat("yyMMdd").format(new Date());
String imageFileName = timeStamp + ".label1";
Log.d(logTag, "jpgFile() imageFileName = "+imageFileName );
File externalFilesDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
Log.d(logTag, "jpgFile() externalFilesDir = "+externalFilesDir );
// create the File object:
if (USE_ANDROID_EXTERNAL_STORAGE_PUBLIC_DIRECTORY) {
// Since getExternalStoragePublicDirectory() has been deprecated in Build.VERSION_CODES.Q and higher:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Log.d(logTag, "jpgFile() Build.VERSION_CODES.Q or higher");
// get the Activity Context:
Context mContext;
mContext = (Context)this;
ContentResolver resolver = mContext.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, imageFileName);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM/" + IMAGES_FOLDER_NAME);
Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
jpgFile = new File(imageUri.getPath());
} else {
Log.d(logTag, "jpgFile() lower than Build.VERSION_CODES.Q");
String stStorageDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DCIM).toString() + File.separator + IMAGES_FOLDER_NAME;
// make sure the directory string points to a directory that exists:
if (!new File(stStorageDir).exists()) { new File(stStorageDir).mkdir(); }
jpgFile = new File(stStorageDir, imageFileName + ".jpg");
}
} else {
//write photos to directory private to this app:
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
jpgFile = new File(storageDir, imageFileName + ".jpg");
}
// Save a file: path for use with ACTION_VIEW intents
stPathToJpgFile = jpgFile.getAbsolutePath();
return jpgFile;
}
/*
Invoke the system's media scanner to add your photo to the Media Provider's database,
making it available in the Android Gallery application and to other apps.
*/
private void galleryAddPic (String stPathToPicFile) {
Log.d(logTag, "galleryAddPic() stPathToPicFile "+stPathToPicFile);
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(stPathToPicFile);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
}
public Uri addImageToGallery(ContentResolver cr, String imgType, File filepath) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.TITLE, "player");
values.put(MediaStore.Images.Media.DISPLAY_NAME, "player");
values.put(MediaStore.Images.Media.DESCRIPTION, "");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/" + imgType);
values.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis());
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
values.put(MediaStore.Images.Media.DATA, filepath.toString());
return cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
}
}

答案1

得分: 2

首先,相册应用无法看到您的照片的原因是因为它保存在这里:
/storage/emulated/0/Android/data/pub.openbook.labellor/files/Pictures

data/ 文件夹是您的应用的私有路径,MediaScanner 将会忽略它。只有当照片不打算通过相册应用查看时,才应该将照片保存在那里。我建议您查阅 数据和文件存储概述 以获取更多信息。

相机应用是否能够保存到公共目录 DCIM 中?

虽然不一定是 "DCIM" 文件夹,但是确实可以将图像保存在设备的默认图片文件夹中。实际上,这通常是相机应用应该使用的文件夹。

如何让我的照片在相册应用中可见?

如果照片文件保存在设备的默认图片文件夹中,在调用 MediaScanner(您的实现是正确的)后,它也应该在任何相册应用中可见。

如果可以的话,怎么做?

与其编写一个自定义示例,我更愿意为您指向 官方有关拍照的文档。我所链接的部分应该正是您所需要的,其中详细解释了所有必要的步骤。基本上,您需要删除当前的 jpgFile() 函数,并将其替换为文档中建议的实现。

需要注意的是,在遵循推荐的实现时,您不需要 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) 这样的代码,因为您正在使用 MediaStore(它也适用于较早的 API)。

英文:

Firstly, the reason Gallery apps do not see your photo is because it's saved here:
/storage/emulated/0/Android/data/pub.openbook.labellor/files/Pictures

The data/<package name> folder is a private path for your app and MediaScanner will ignore it. You should only save photos there if they're not meant to be seen with the Gallery app. I recommend taking a look at the data and file storage overview to learn more.

> Is it even possible to have the Camera App save to Public Directory DCIM?

It isn't necessarily "DCIM", but it is possible to save the image in the device's default pictures folder, yes. In fact, this is the usual folder you should use for a camera app.

> How do I make my the photo visible in the Gallery App?

If the photo file is saved in the device's default pictures folder it should also be visible in any Gallery App after invoking the MediaScanner (your implementation is fine).

> If so, how?

Rather than writing a custom example, I'd like to point you towards the official docs for taking photos. The section I linked should be exactly what you need, with a thorough explanation of all the required steps. Basically, you want to remove your current jpgFile() function and replace it with the suggested implementation from the docs.

Note that, when following the recommended implementation, you don't need the if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.Q) stuff since you're using MediaStore (which also works with earlier APIs).

huangapple
  • 本文由 发表于 2020年7月26日 17:08:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/63098192.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定