Flutter开源项目——Android免费壁纸应用

简介

free_wallpaper是一款基于flutter的免费Android壁纸应用

项目明细

开发环境:
android studio 3.5
Flutter 1.12.13+hotfix.7 • channel stable
Framework • revision 9f5ff2306b
Engine • revision a67792536c
Tools • Dart 2.7.0

主要功能

1.分端浏览
2.筛选功能
3.搜索功能
4.搜索历史记录
5.下载壁纸和设置壁纸
6.下载管理
(更多功能还在陆续填坑中...)

项目特点

本项目采用Serverless模式,使用dio网络请求库和jsoup获取数据。使用百度图片接口实现搜索功能;使用CachedNetworkImage加载网络图片;使用Android插件的形式实现了部分Android原生功能,如设置壁纸和更新MediaStore等。

部分代码

1.WallpaperPlugin(修改自wallpaper插件,解决了dio库版本不兼容的问题,添加了设置本地图片的功能)

import android.Manifest
import android.annotation.TargetApi
import android.app.Activity
import android.app.WallpaperManager
import android.content.ActivityNotFoundException
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import io.flutter.Log
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import java.io.File
import java.io.IOException


/** WallpaperPlugin  */
class WallpaperPlugin constructor(var mContext: Context): FlutterActivity(), FlutterPlugin, MethodCallHandler {
    private var id = 0
    private var res = ""
    private var channel: MethodChannel?=null


    @TargetApi(Build.VERSION_CODES.FROYO)
    private fun setWallpaper(i: Int, imagePath: String): String {
        id = i
        val wallpaperManager = WallpaperManager.getInstance(mContext)
        val file = File(imagePath)
        // set bitmap to wallpaper
        val bitmap = BitmapFactory.decodeFile(file.absolutePath)
        if (id == 1) {
            try {
                res = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    wallpaperManager.setBitmap(bitmap, null, true, WallpaperManager.FLAG_SYSTEM)
                    "Home Screen Set Successfully"
                } else {
                    "To Set Home Screen Requires Api Level 24"
                }
            } catch (ex: IOException) {
                ex.printStackTrace()
            }
        } else if (id == 2) try {
            res = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                wallpaperManager.setBitmap(bitmap, null, true, WallpaperManager.FLAG_LOCK)
                "Lock Screen Set Successfully"
            } else {
                "To Set Lock Screen Requires Api Level 24"
            }
        } catch (e: IOException) {
            res = e.toString()
            e.printStackTrace()
        } else if (id == 3) {
            try {
                wallpaperManager.setBitmap(bitmap)
                res = "Home And Lock Screen Set Successfully"
            } catch (e: IOException) {
                res = e.toString()
                e.printStackTrace()
            }
        } else if (id == 4) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (activity.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
                        != PackageManager.PERMISSION_GRANTED &&
                        activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                        != PackageManager.PERMISSION_GRANTED) {
                    activity.requestPermissions(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE,
                            Manifest.permission.WRITE_EXTERNAL_STORAGE), 1)
                } else {
                    Uri.fromFile(file)
                    val contentURI = getImageContentUri(this, file)
                    val intent = Intent(wallpaperManager.getCropAndSetWallpaperIntent(contentURI))
                    val mime = "image/*"
                    intent.setDataAndType(contentURI, mime)
                    try {
                        startActivityForResult(intent, 2)
                    } catch (e: ActivityNotFoundException) { //handle error
                        res = "Error To Set Wallpaer"
                    }
                }
            }
        }
        return res
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
        Log.d("Tag", "resultcode=" + resultCode + "requestcode=" + requestCode)
        res = when (resultCode) {
            Activity.RESULT_OK -> {
                "System Screen Set Successfully"
            }
            Activity.RESULT_CANCELED -> {
                "setting Wallpaper Cancelled"
            }
            else -> {
                "Something Went Wrong"
            }
        }
        super.onActivityResult(requestCode, resultCode, data)
    }




    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
        when (call.method) {
            "getPlatformVersion" -> result.success("" + Build.VERSION.RELEASE)
            "HomeScreen" -> result.success(setWallpaper(1, call.arguments as String))
            "LockScreen" -> result.success(setWallpaper(2, call.arguments as String))
            "Both" -> result.success(setWallpaper(3, call.arguments as String))
            "SystemWallpaer" -> result.success(setWallpaper(4, call.arguments as String))
            else -> result.notImplemented()
        }
    }


    override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        channel = MethodChannel(binding.binaryMessenger, "WallpaperPlugin")
        channel!!.setMethodCallHandler(WallpaperPlugin(mContext))

    }

    override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        channel!!.setMethodCallHandler(null)
        channel=null
    }

    companion object {

        fun getImageContentUri(context: Context, imageFile: File): Uri? {
            val filePath = imageFile.absolutePath
            Log.d("Tag", filePath)
            val cursor = context.contentResolver.query(
                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI, arrayOf(MediaStore.Images.Media._ID),
                    MediaStore.Images.Media.DATA + "=? ", arrayOf(filePath), null)
            return if (cursor != null && cursor.moveToFirst()) {
                val id = cursor.getInt(cursor
                        .getColumnIndex(MediaStore.MediaColumns._ID))
                val baseUri = Uri.parse("content://media/external/images/media")
                Uri.withAppendedPath(baseUri, "" + id)
            } else {
                if (imageFile.exists()) {
                    val values = ContentValues()
                    values.put(MediaStore.Images.Media.DATA, filePath)
                    context.contentResolver.insert(
                            MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
                } else {
                    null
                }
            }
        }
    }

}

2.MediaStorePlugin(更新本机媒体库)

import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import android.net.Uri
import android.provider.MediaStore
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import java.io.File


/**
 * description:
 * author:luoxingyuan
 */
class MediaStorePlugin constructor(var mContext: Context) : FlutterPlugin, MethodChannel.MethodCallHandler {
    private var channel: MethodChannel?=null
    override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        channel = MethodChannel(binding.binaryMessenger, "MediaStorePlugin")
        channel!!.setMethodCallHandler(MediaStorePlugin(mContext))
    }

    override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        channel!!.setMethodCallHandler(null)
        channel=null
    }

    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
        when (call.method) {
            "refreshMediaStore" -> result.success(sendMediaBroadcast(call.arguments as String))
            else -> result.notImplemented()
        }
    }

    private fun sendMediaBroadcast(filePath: String) {
        val file = File(filePath)
        //通知相册更新
        MediaStore.Images.Media.insertImage(mContext.contentResolver, BitmapFactory.decodeFile(file.absolutePath), file.name, null)
        val intent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)
        val uri: Uri = Uri.fromFile(file)
        intent.data = uri
        mContext.sendBroadcast(intent)
    }

}

3.PC端壁纸分类列表

import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:free_wallpaper/model/category_model.dart';
import 'package:free_wallpaper/net/address.dart';
import 'package:free_wallpaper/net/http_callback.dart';
import 'package:free_wallpaper/net/http_manager.dart';
import 'package:free_wallpaper/net/result_data.dart';
import 'package:free_wallpaper/utils/toast.dart';
import 'package:free_wallpaper/widget/error_placeholder.dart';
import 'package:free_wallpaper/widget/loading_dialog.dart';
import 'package:html/parser.dart' show parse;

import 'page_albums.dart';
/*
  description:
  author:59432
  create_time:2020/1/22 12:59
*/

class CategoriesPage extends StatefulWidget {
  @override
  State createState() => CategoriesPageState();
}

class CategoriesPageState extends State {
  var categories = List();

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _requestData(showLoading: true);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body:  RefreshIndicator(
          color: Colors.pinkAccent,
          backgroundColor: Colors.white,
          child: Container(
            margin: const EdgeInsets.only(left: 8.0, right: 8, top: 8),
            child: GridView.count(
              // Create a grid with 2 columns. If you change the scrollDirection to
              // horizontal, this produces 2 rows.
              crossAxisCount: 3,
              crossAxisSpacing: 8.0,
              mainAxisSpacing: 8,
              // Generate 100 widgets that display their index in the List.
              children: List.generate(categories.length, (index) {
                return _buildItem(context, categories[index]);
              }),
            ),
          ), onRefresh: _refreshData),
    );
  }

  _requestData({showLoading = false}) {
    HttpManager.getInstance(baseUrl: Address.MEI_ZHUO)
        .getHtml("/zt/index.html", HttpCallback(
        onStart: () {
          if (showLoading) {
            LoadingDialog.showProgress(context);
          }
        },
        onSuccess: (ResultData data) {
          if (showLoading) {
            LoadingDialog.dismiss(context);
          }
          var doc = parse(data.data);
          var aTags = doc.body
              .getElementsByClassName("nr_zt w1180")
              .first
              .getElementsByTagName("a");
          categories.clear();
          aTags.forEach((a) {
            var href = a.attributes["href"];
            var src = a
                .querySelector("img")
                .attributes["src"];
            var category = a
                .querySelector("p")
                .text;
            categories.add(CategoryModel(name: category, href: href, src: src));
          });

          setState(() {

          });
        },
        onError: (ResultData error) {
          if (showLoading) {
            LoadingDialog.dismiss(context);
          }
          ToastUtil.showToast(error.data);
        }
    ));
  }

  Widget _buildItem(BuildContext context, CategoryModel category) {
    return  GestureDetector(
      onTap: () => _onItemClick(category),
      child: ClipOval(
        child: Stack(
          alignment: const Alignment(0.0, 1.0),
          children: [
            CachedNetworkImage(
              imageUrl: category.src,
              placeholder: (context, url) => Center(child: CircularProgressIndicator()),
              errorWidget: (context, url, error) => ErrorPlaceHolder(),
              fit: BoxFit.fill,
              height: (MediaQuery
                  .of(context)
                  .size
                  .width) / 3,
            ),
            Container( //分析 4
              width: (MediaQuery
                  .of(context)
                  .size
                  .width) / 3,
              decoration:  BoxDecoration(
                color: Colors.black45,
              ),
              child: Text(
                category.name,
                textAlign: TextAlign.center,
                style: TextStyle(
                  fontSize: 16.0,
                  color: Colors.white,
                ),
              ),
            ),
          ],

        ),
      ),
    );
  }

  _onItemClick(CategoryModel category) {
    Navigator.push(
      context,
       MaterialPageRoute(builder: (context) =>  AlbumsPage(category, false)),
    );
  }

  Future _refreshData() async {
    _requestData();
  }
}

预览

Screenshot_2020-02-11-18-09-59-827_wallpaper.cn.mewlxy.free_wallpaper.jpg
Screenshot_2020-02-11-18-10-13-965_wallpaper.cn.mewlxy.free_wallpaper.jpg
Screenshot_2020-02-11-18-11-12-049_wallpaper.cn.mewlxy.free_wallpaper.jpg
Screenshot_2020-02-11-18-11-48-216_wallpaper.cn.mewlxy.free_wallpaper.jpg

项目地址

https://github.com/lxygithub/free_wallpaper

你可能感兴趣的:(Flutter开源项目——Android免费壁纸应用)